VMware Cloud Community
TravisBarker
Contributor
Contributor

Null value error when executing snapshotreminder.ps1

I am looking for help in troubleshooting some errors that I am getting while running running Virtu-Al's snaphotreminder.ps1 script.  I get the following error everytime I run the script:

Exception calling “Add” with “1″ argument(s): “Value cannot be null.
Parameter name: item”
At C:\Users\gateway\Desktop\snap.ps1:86 char:13
+     $msg.To.Add <<<< ($Mailto)
+ CategoryInfo          : NotSpecified: (:) [], MethodInvocationException
+ FullyQualifiedErrorId : DotNetMethodException

Exception calling "Send" with "1" argument(s): "A recipient must be specified."
At C:\Users\gateway\Desktop\snap.ps1:99 char:12
+     $smtp.Send <<<< ($msg)
+ CategoryInfo          : NotSpecified: (:) [], MethodInvocationException
+ FullyQualifiedErrorId : DotNetMethodException

The error looks to be have to do with the fact that the Snapshot Creator information is not being read for some reason.   Any help would be greatly appreciated.  Thanks.

Tags (2)
0 Kudos
13 Replies
Troy_Clavell
Immortal
Immortal

did you define the variables?

# Please use the below variables to define your settings before use#
$smtpServer = "mysmtpserver.mydomain.com"
$MailFrom = "me@mydomain.com"
$VISRV = "MYVISERVER"

Also, if you have no email field setup in AD, it may fail

However, I'm no expert here, so I could be wrong.

0 Kudos
TravisBarker
Contributor
Contributor

Thanks for the response.  I should have put in more info into the original post.  Here is the script that I am working with:

# - SnapReminder V1.0 By Virtu-Al - http://virtu-al.net
#
# Please use the below variables to define your settings before use
#
$smtpServer = "mysmtpserver"
$MailFrom = "myadminemailaddress"
$VISRV1 = "myvcenterserver.1"
$VISRV2 = "myvcenterserver.2"

function Find-User ($username){
    if ($username -ne $null)
    {
        $usr = (($username.split("\"))[1])
        $root = [ADSI]""
        $filter = ("(&(objectCategory=user)(samAccountName=$Usr))")
        $ds = new-object system.DirectoryServices.DirectorySearcher($root,$filter)
        $ds.PageSize = 1000
        $ds.FindOne()
    }
}

function Get-SnapshotTree{
    param($tree, $target)

    $found = $null
    foreach($elem in $tree){
        if($elem.Snapshot.Value -eq $target.Value){
            $found = $elem
            continue
        }
    }
    if($found -eq $null -and $elem.ChildSnapshotList -ne $null){
        $found = Get-SnapshotTree $elem.ChildSnapshotList $target
    }

    return $found
}

function Get-SnapshotExtra ($snap){
    $guestName = $snap.VM   # The name of the guest

    $tasknumber = 999       # Windowsize of the Task collector

    $taskMgr = Get-View TaskManager

    # Create hash table. Each entry is a create snapshot task
    $report = @{}

    $filter = New-Object VMware.Vim.TaskFilterSpec
    $filter.Time = New-Object VMware.Vim.TaskFilterSpecByTime
    $filter.Time.beginTime = (($snap.Created).AddSeconds(-5))
    $filter.Time.timeType = "startedTime"

    $collectionImpl = Get-View ($taskMgr.CreateCollectorForTasks($filter))

    $dummy = $collectionImpl.RewindCollector
    $collection = $collectionImpl.ReadNextTasks($tasknumber)
    while($collection -ne $null){
        $collection | where {$_.DescriptionId -eq "VirtualMachine.createSnapshot" -and $_.State -eq "success" -and $_.EntityName -eq $guestName} | %{
            $row = New-Object PsObject
            $row | Add-Member -MemberType NoteProperty -Name User -Value $_.Reason.UserName
            $vm = Get-View $_.Entity
            $snapshot = Get-SnapshotTree $vm.Snapshot.RootSnapshotList $_.Result
            $key = $_.EntityName + "&" + ($snapshot.CreateTime.ToString())
            $report[$key] = $row
        }
        $collection = $collectionImpl.ReadNextTasks($tasknumber)
    }
    $collectionImpl.DestroyCollector()

    # Get the guest's snapshots and add the user
    $snapshotsExtra = $snap | % {
        $key = $_.vm.Name + "&" + ($_.Created.ToString())
        if($report.ContainsKey($key)){
            $_ | Add-Member -MemberType NoteProperty -Name Creator -Value $report[$key].User
        }
        $_
    }
    $snapshotsExtra
}

Function SnapMail ($Mailto, $snapshot)
{
    $msg = new-object Net.Mail.MailMessage
    $smtp = new-object Net.Mail.SmtpClient($smtpServer)
    $msg.From = $MailFrom
    $msg.To.Add($Mailto)

    $msg.Subject = "Snapshot Reminder for " + $snapshot.VM

$MailText = @"
This is a reminder that you have a snapshot active on $($snapshot.VM) which was taken on $($snapshot.Created).

If you are not actively using a snapshot, please delete it or revert to it.  If the snapshot is currently needed, please disregard this message.

Please keep in mind that Snapshots are not a replacement for Backups.

Snapshot Name: $($snapshot.Name)

Snapshot Description: $($snapshot.Description)


"@

    $msg.Body = $MailText
    $smtp.Send($msg)
}

Connect-VIServer $VISRV1

foreach ($snap in (Get-VM | Get-Snapshot | Where {$_.Created -lt ((Get-Date).AddDays(-0))})){
    $SnapshotInfo = Get-SnapshotExtra $snap
    $mailto = ((Find-User $SnapshotInfo.Creator).Properties.mail)
    SnapMail $mailto $SnapshotInfo
}

Disconnect-VIServer -Confirm:$False
Connect-VIServer $VISRV2

foreach ($snap in (Get-VM | Get-Snapshot | Where {$_.Created -lt ((Get-Date).AddDays(-0))})){
    $SnapshotInfo = Get-SnapshotExtra $snap
    $mailto = ((Find-User $SnapshotInfo.Creator).Properties.mail)
    SnapMail $mailto $SnapshotInfo
}

Disconnect-VIServer -Confirm:$False

0 Kudos
LucD
Leadership
Leadership

The main foreach loop appears twice in your code (see the last lines).

Is that a copy/paste error ?


Blog: lucd.info  Twitter: @LucD22  Co-author PowerCLI Reference

0 Kudos
TravisBarker
Contributor
Contributor

I had edited the script to connect and run against two vcenter servers instead of one.

0 Kudos
LucD
Leadership
Leadership

It looks as if the assignment of the To property in the line

$msg.To.Add($Mailto)

is done with an empty $Mailto variable.

This can be caused by the fact that the script didn't find the event for the creation of the snapshot or that the account that present in the event, can't be found in Active Directory.

You can change this line

$msg.To.Add($Mailto)

to this

if($Mailto){
   $msg.To.Add($Mailto)
}
else{
   $msg.To.Add($MailFrom)
}

This will send the email about the snapshot to the address specified in the $MailFrom variable if the $Mailto variable is empty.

You don't have to repeat that main foreach loop.

You can connect to more than 1 vCenter at a time.

Like this

Connect-VIServer $VISRV1,$VISRV2


Blog: lucd.info  Twitter: @LucD22  Co-author PowerCLI Reference

TravisBarker
Contributor
Contributor

Thanks LucD for shedding some light on what might be behind the lack of info in the $mailto attribute and showing that it is possible to connect and run against multple vcenters.

The null value is happening with all snapshots not just here or there.  I have gone over the data on the AD side and all looks good.  Any ideas on what might be a root cause of the creation event not being found?

Powershell is pretty new to me and your help is greatly appriciated.

0 Kudos
LucD
Leadership
Leadership

One of the reasons could be that your vCenter(s) don't retain the events for a long time or perhaps even not at all.

Check in a vCenter with the vSphere Client and go to <Administration><vCenter Server Settings><Database Retention Policy>.

Another reason could be that the creation data for the event is too far away from the creation date in the snapshot.

The script uses a 5 seonds delta to find the event that belongs to a snapshot.

Perhaps that is not long enough.


Blog: lucd.info  Twitter: @LucD22  Co-author PowerCLI Reference

0 Kudos
kevo12
Enthusiast
Enthusiast

In my companies environment, admistrative accounts are used which have a "_adm" suffix and the accounts do not have mailboxes in AD. So in my case, the $Mailto entry would be blank and the work-around that LucD provide got this working for me. (Hurray)

However, I still have a desire to know who it was that created the snapshot. I have a text cluster and verify that the snapshot creation event is logged with the ADM account. Is there anyway to capture who created the snapshot and add it to the text of the email.

I know it's wrong, but maybe something like the below?

$MailText = @"

VMName: $($snapshot.VM)

Snapshot Name: $($snapshot.Name)
Snapshot taken on: $($snapshot.Created)
Snapshot Created By: $($snapshotsExtraCreator)

User: SnapMail ($Mailto)
Description: $($snapshot.Description)

"@ 

mucho thanks,

Kev

0 Kudos
LucD
Leadership
Leadership

That information is already present,

Just change the mailbody to

 $MailText = @"
VMName: $($snapshot.VM)

Snapshot Name: $($snapshot.Name)
Snapshot taken on: $($snapshot.Created)
Snapshot Created By: $($snapshot.Creator)
User: $($Mailto)
Description: $($snapshot.Description)

"@


Blog: lucd.info  Twitter: @LucD22  Co-author PowerCLI Reference

0 Kudos
kevo12
Enthusiast
Enthusiast

Thanks for the reply, but no dice on my end. $Mailto is not returning anything.

For whatever reason, it seems that the $username is returning a null value and I dont understand why. I added the below (2) out-file commands to the Find-User function;  the username.txt file is gets created but is blank while the usr.txt does not get created at all (prob b/c $username is null).

Any ideas as to why Im unable to see who created the snapshot? I am able to see who created it in VC..   VC is 4.0

function Find-User ($username){
$username | Out-file c:\temp\username.txt
     if ($username -ne $null)
        {
        $usr = (($username.split("\"))[1])

$usr | Out-file c:\temp\usr.txt

thanks for your help!

0 Kudos
kevo12
Enthusiast
Enthusiast

Im still stuck, but im thinking this may be a time zone issue. I piped $key to an out-file and it reports the following.

code:

$key = $_.EntityName + "&" + ($snapshot.CreateTime.ToString())
            $key | Out-File c:\Temp\key.txt

results:

testvm1&8/22/2011 3:06:31 PM

But the snapshot was actually take a 11:06:31 AM... And our host are using GMT, so that explains the time difference that I am seeing.  I though I could simlply adjusting the below value to go forward in time (4hours), but im having no luck with that either.

$filter.Time.beginTime = (($snap.Created).AddSeconds(+18000))

Any good idea on how I can get around this?

thanks Again!!

Kev

0 Kudos
LucD
Leadership
Leadership

You could convert all the dates to UTC as follows

$snap.Created.ToUniversalTime()


Blog: lucd.info  Twitter: @LucD22  Co-author PowerCLI Reference

0 Kudos
kevo12
Enthusiast
Enthusiast

Thanks again LucD!!!

After adding  the string  ToUniversalTime() to both of the below line, script runs great. Prior to this, my work-around was to run the script from a machine that had its time set to GMT.

$filter.Time.beginTime = (($snap.Created.ToUniversalTime()).Addseconds(-5))

$key = $_.vm.Name + "&" + ($_.Created.ToUniversalTime().ToString())

0 Kudos