PowerShell script exchange
Post Reply
Backerupper
Enthusiast
Posts: 47
Liked: 24 times
Joined: Dec 28, 2012 2:32 pm
Contact:

My Veeam Report turns 2.0!

Post by Backerupper » 3 people like this post

I know many folks on this forum have used my scripts before and I certainly have gotten tons of information for them from these forums, so I thought I would let everyone know that a new version of My Veeam Report has been posted.

It's a bit large to post here so I'll just drop a link to the blog post.
http://wp.me/p3X47Z-EUo

The report is designed with a VMware environment in mind. It covers VM backups, VM Restores & Endpoint backups over a given amount of time, Repo's, Proxies, Windows Services and keeping tabs on license expiration.

Any and all feedback is always welcomed.

*A special thank you goes to tsightler for all your help!
veremin
Product Manager
Posts: 20400
Liked: 2298 times
Joined: Oct 26, 2012 3:28 pm
Full Name: Vladimir Eremin
Contact:

Re: My Veeam Report turns 2.0!

Post by veremin »

Hi,

I've checked the code briefly and have to say that overall it looks great. I think, though, there are some things that might be improved:

1) Find-VBRObject. This is obsolete commandlet that has been replaced with Find-VBRViEntity and Find-VBRHvEntity. It stands to reason to leverage the latter cmdlets just to be sure that the script is not based on something old that will cease to exist in the nearest future.

2) Get-VeeamVersion. May be you can make the said function utilize this approach. So that hard coded variable containing path to the corresponding .exe file won't be required any longer.

By the way, I'm wondering what particular information is missing in VB&R PS Endpoint Session.

Thanks.
Backerupper
Enthusiast
Posts: 47
Liked: 24 times
Joined: Dec 28, 2012 2:32 pm
Contact:

Re: My Veeam Report turns 2.0!

Post by Backerupper »

Hi Vladimir

Thanks for the suggestions!

Re: Find-VBRObject - This function (Get-VMsBackupStatus) was originally written by Tom Sightler (like many great scripts!) back in 2012. There very well may be a much better way to retrieve the same results at this point. My attempts at converting it over to using Find-VBRViEntity have been unsuccessful to date. The biggest reason being the two commands return different object types with different properties and methods. So it's not just an easy swap. If you (or anyone here) could shed some light on how to rewrite the function it would be very much appreciated as the results of such a function are highly sought-after.

Re: Get-VeeamVersion - excellent idea! In fact I should be able to find the location of the other file needed using this method as well.

Re: Endpoint sessions - There are two omissions I feel would be beneficial and are included with all other types of sessions (that I am aware of). The first being Get-VBREPSession does not return a Progress property as the others do. This typically includes things like Total Size, Processed Size, Read Size, Transferred Size, Avg Speed and Percent Complete. The other missing piece is the GetDetails method. This method typically allows us to see the details of the session, particularly when a session fails - it tells us why. It is obvious VBR tracks all this info as it is within the email sent after job completion (though oddly omitted from the console as well). It is of course, very possible I just haven't found where to pull this information from, if I am missing something please let me know.

Thanks again!
tsightler
VP, Product Management
Posts: 6035
Liked: 2860 times
Joined: Jun 05, 2009 12:57 pm
Full Name: Tom Sightler
Contact:

Re: My Veeam Report turns 2.0!

Post by tsightler » 1 person likes this post

Later in the week I'll try to take a look at Get-VMsBackupStatus and see how it might possibly be converted to no longer use Find-VBRObject. It's been quite a while since I've worked with that code.
Backerupper
Enthusiast
Posts: 47
Liked: 24 times
Joined: Dec 28, 2012 2:32 pm
Contact:

Re: My Veeam Report turns 2.0!

Post by Backerupper »

Hi Tom

Any chance of you taking a look at reworking this function?

<pretty please :lol: >

Thanks!!
tsightler
VP, Product Management
Posts: 6035
Liked: 2860 times
Joined: Jun 05, 2009 12:57 pm
Full Name: Tom Sightler
Contact:

Re: My Veeam Report turns 2.0!

Post by tsightler »

Here's a crack at it. I've tested it against a few labs I have access to and it seems to work but certainly there could be bugs lurking. I'd love to rewrite this completely, I think I could do much better now as my Powershell skills have improved dramatically since I wrote this original code. However, due to lack of time for testing and knowing that the old code has been run in lots of environments to this point, I decided to keep the original logic and just adapt the code as required for the new object type returned by Find-VBRViEntity. This was fairly simple, other than trying to remember why I did all of this stuff to begin with.

One thing to note is that the code that was previously filling $jobobjids was making an unsupported call directly to an internal method. In the code below I've replaced this unsupported call with a simple call to the FindObject() method on the objects returned by Find-VBRViEntity. However, I can't remember exactly when this method was introduced and I only had v9 versions of B&R to test against. It's possible it's been there for a long time but I left the old function in the code simply remarked out so if you find the new code doesn't work on older B&R versions you can just swap out that line with the prior one.

Another small change is that, if a host is not in a cluster, the previous code returned null for the Cluster Name, but the new code should return the hostname itself instead. I consider this change an enhancement, but in reality it was mostly just a shortcut because I couldn't quickly figure out how to get the old behavior without some additional logic.

Code: Select all

Function Get-VMsBackupStatus {
    param (
	  [String]$vcenter)
	  
	# Convert exclusion list to simple regular expression
	$excludevms_regex = ('(?i)^(' + (($script:excludeVMs | ForEach {[regex]::escape($_)}) -join "|") + ')$') -replace "\\\*", ".*"
	$excludefolder_regex = ('(?i)^(' + (($script:excludeFolder | ForEach {[regex]::escape($_)}) -join "|") + ')$') -replace "\\\*", ".*"
	$excludedc_regex = ('(?i)^(' + (($script:excludeDC | ForEach {[regex]::escape($_)}) -join "|") + ')$') -replace "\\\*", ".*"
	
	$outputary = @() 
	$vcenterobj = Get-VBRServer -Name $vcenter
	$vmobjs = Find-VBRViEntity -Server $vcenterobj | 
		Where-Object {$_.Type -eq "Vm" -and $_.VmFolderName -notmatch $excludefolder_regex} |
		Where-Object {$_.Name -notmatch $excludevms_regex} |
		Where-Object {$_.Path.Split("\")[1] -notmatch $excludedc_regex}
		
		
	#$jobobjids = [Veeam.Backup.Core.CHierarchyObj]::GetObjectsOnHost($vcenterobj.id) | Where-Object {$_.Type -eq "Vm"}
    $jobobjids = $vmobjs.FindObject()

	Foreach ($vm in $vmobjs) {
		$jobobjid = ($jobobjids | Where-Object {$_.ObjectId -eq $vm.Reference}).Id
		If (!$jobobjid) {
			$jobobjid = $vm.Id
		}
		$vm | Add-Member -MemberType NoteProperty "JobObjId" -Value $jobobjid
	}    
		
	# Get a list of all VMs from vCenter and add to hash table, assume Unprotected
	$vms=@{}
	Foreach ($vm in $vmobjs)  {
		If(!$vms.ContainsKey($vm.JobObjId)) {
			$vmdc = $vm.Path.Split("\")[1]
			$vmclus = $vm.Path.Split("\")[2]
			$vms.Add($vm.JobObjId, @("!", $vmdc, $vmclus, $vm.Name))
		}
	}

	# Find all backup job sessions that have ended in the last x hours
	$vbrsessions = Get-VBRBackupSession | Where-Object {$_.JobType -eq "Backup" -and $_.EndTime -ge (Get-Date).addhours(-$script:HourstoCheck)}

	# Find all Successfuly backed up VMs in selected sessions (i.e. VMs not ending in failure) and update status to "Protected"
	If ($vbrsessions) {
		Foreach ($session in $vbrsessions) {
			Foreach ($vm in ($session.gettasksessions() | Where-Object {$_.Status -ne "Failed"} | ForEach-Object { $_ })) {
				If($vms.ContainsKey($vm.Info.ObjectId)) {
					$vms[$vm.Info.ObjectId][0]=$session.JobName
				}
			}
		}
	}
	$vms.GetEnumerator() | Sort-Object Value
}
Backerupper
Enthusiast
Posts: 47
Liked: 24 times
Joined: Dec 28, 2012 2:32 pm
Contact:

Re: My Veeam Report turns 2.0!

Post by Backerupper »

You rock!

For some reason, and the first time ever, I can actually follow the logic now.
I'll do a bit more testing myself but so far so good.

I (and I'm sure many others) appreciate the time you spend helping us out.
tsightler
VP, Product Management
Posts: 6035
Liked: 2860 times
Joined: Jun 05, 2009 12:57 pm
Full Name: Tom Sightler
Contact:

Re: My Veeam Report turns 2.0!

Post by tsightler »

If I find time (and motivation) I may try to clean this up. I'm pretty sure I could eliminate at least one loop (probably two) and just load the data into a hash table directly the first time, should speed it up significantly and drastically simplify the code, making it even easier to follow.
tsightler
VP, Product Management
Posts: 6035
Liked: 2860 times
Joined: Jun 05, 2009 12:57 pm
Full Name: Tom Sightler
Contact:

Re: My Veeam Report turns 2.0!

Post by tsightler » 1 person likes this post

OK, so here's a simplified version. Overall logic is the same, but I removed multiple useless loops and also a bunch of the variables. This cuts out some time for larger environments, and significantly reduces the required memory as well.

So far I've not been able to find any significant issues with this code. There is a small difference in behavior from the old code code which used Find-VBRObject. In the old code templates were included in the report while this new code excludes those. I'm not sure which behavior is preferred but I'm sure I can address that as I'm guessing it's just being excluded by the filter on Find-VBRViEntity. If you'd prefer the old behavior of including templates just let me know and I can look into it.

Code: Select all

Function Get-VMsBackupStatus {
    param (
     [String]$vcenter)
     
   # Convert exclusion list to simple regular expression
   $excludevms_regex = ('(?i)^(' + (($script:excludeVMs | ForEach {[regex]::escape($_)}) -join "|") + ')$') -replace "\\\*", ".*"
   $excludefolder_regex = ('(?i)^(' + (($script:excludeFolder | ForEach {[regex]::escape($_)}) -join "|") + ')$') -replace "\\\*", ".*"
   $excludedc_regex = ('(?i)^(' + (($script:excludeDC | ForEach {[regex]::escape($_)}) -join "|") + ')$') -replace "\\\*", ".*"
   
    $outputary = @() 
    $vcenterobj = Get-VBRServer -Name $vcenter
    $vms=@{}

    # Build a hash table of all VMs.  Key is either Job Object Id (for any VM ever in a Veeam job) or vCenter ID+MoRef
    # Assume unprotected (!), and populate Cluster, DataCenter, and Name fields for hash key value
    Find-VBRViEntity -Server $vcenterobj | 
       Where-Object {$_.Type -eq "Vm" -and $_.VmFolderName -notmatch $excludefolder_regex} |
        Where-Object {$_.Name -notmatch $excludevms_regex} |
        Where-Object {$_.Path.Split("\")[1] -notmatch $excludedc_regex} |
        ForEach {$vms.Add(($_.FindObject().Id, $_.Id -ne $null)[0], @("!", $_.Path.Split("\")[1], $_.Path.Split("\")[2], $_.Name))}
      
   # Find all backup task sessions that have ended in the last x hours and not ending in Failure
   $vbrtasksessions = (Get-VBRBackupSession | 
        Where-Object {$_.JobType -eq "Backup" -and $_.EndTime -ge (Get-Date).addhours(-$script:HourstoCheck)}) |
        Get-VBRTaskSession | Where-Object {$_.Status -ne "Failed"}

   # Compare VM list to session list and update found VMs status to "Protected"
   If ($vbrtasksessions) {
      Foreach ($vmtask in $vbrtasksessions) {
         If($vms.ContainsKey($vmtask.Info.ObjectId)) {
            $vms[$vmtask.Info.ObjectId][0]=$vmtask.JobName
         }
      }
   }
   $vms.GetEnumerator() | Sort-Object Value
}
Backerupper
Enthusiast
Posts: 47
Liked: 24 times
Joined: Dec 28, 2012 2:32 pm
Contact:

Re: My Veeam Report turns 2.0!

Post by Backerupper »

Great stuff!

(Again, thanks for taking the time!)

The template situation does pose a bit of a issue though - would like to include them if at all possible.
But things seem to get ugly...

Here's what I have found:
Find-VBRViEntity by itself will:
a) return everything EXCEPT templates
b) the Path property is in 'host and cluster view' - vcenter\datacenter\cluster (or host)\vm

Find-VBRViEntity -VMsAndTemplates will change the behavior:
a) returns VMs and Templates but EXCLUDES VMs in vApps (vcenter type vApps - think vCOps - UI VM and Analytics VM are excluded)
b) the Path property is in Folder view - vcenter\datacenter\toplevelfolder\subfolder\subfolder\vm

I can't seem to find a use of Find-VBRViEntity that will return everything including templates and vApp VMs in one shot.

I could do two queries and combine them (one using no switch and another just getting the templates from -VMsAndTemplates) but then the Path property is showing different info for VMs vs Templates. This isn't necessarily the end of the world but makes it difficult to pull out the info letting you know where the vm resides (especially useful in large environments). The only common location info between the two would be vCenter, Datacenter and Folder - which in a large environment doesn't really give enough info to highlight where that VM/Template exists.
In fact this could result in two VMs reporting the same info back:
VM1 - vcenter\datacenter\client1\servers\testvm
VM2 - vcenter\datacenter\client2\servers\testvm
Notice vcenter, datacenter, folder and vm name would all be the same in this case
If the report returns only one of the VMs as not being backed up - which one failed?

I suppose this isn't that far off from where we started as this could have happened there as well - as we were only returning datacenter, cluster and VM name.

Thoughts?
tsightler
VP, Product Management
Posts: 6035
Liked: 2860 times
Joined: Jun 05, 2009 12:57 pm
Full Name: Tom Sightler
Contact:

Re: My Veeam Report turns 2.0!

Post by tsightler »

Yeah, I had glanced at it as well and came to the same conclusion, but I didn't have time to really dig at it last night. However, after thinking about it today I think there are a couple of options. One, the hostname is available in the template entity object so, for the second query that grabs the templates, you could do something like:

Code: Select all

Find-VBRViEntity -VMsandTemplates -Server $vcenterobj |
    Where-Object {$_.Type -eq "Vm" -and $_.IsTemplate -eq "True" -and $_.VmFolderName -notmatch $excludefolder_regex} |
    Where-Object {$_.Name -notmatch $excludevms_regex} |
    Where-Object {$_.Path.Split("\")[1] -notmatch $excludedc_regex} |
    ForEach {$vms.Add(($_.FindObject().Id, $_.Id -ne $null)[0], @("!", $_.Path.Split("\")[1], $_.VmHostName, $_.Name))}
This would print the hostname that the template is homed on, rather than the cluster, but is a template really part of a cluster anyway? It doesn't show up under clusters anywhere in the VMware GUI does it? So perhaps using the hostname is more a more accurate representation. But if you do think the cluster name is preferred even for templates then it should be easy enough to figure out what cluster the host is in since we have the host name, just a little more logic.

I don't like having to run the Find-VBRViEntity code twice though, because it's so heavy (i.e. slow), but perhaps I can figure out a way to get everything in a single call once we settle on what information should be presenting for templates.
Backerupper
Enthusiast
Posts: 47
Liked: 24 times
Joined: Dec 28, 2012 2:32 pm
Contact:

Re: My Veeam Report turns 2.0!

Post by Backerupper »

I think we have got the best we can with what we have to work with.

It does take a bit of time to run, especially if you are running against multiple vcenters (2x Find-VBREntity for each vcenter).
The extra minute or two doesn't matter much for me as I use this in a scheduled report - though it would be nice to reduce if we could.

Here's the final function:

Code: Select all

Function Get-VMsBackupStatus {
param (
     [String]$vcenter)
     
    # Convert exclusion list to simple regular expression
    $excludevms_regex = ('(?i)^(' + (($script:excludeVMs | ForEach {[regex]::escape($_)}) -join "|") + ')$') -replace "\\\*", ".*"
    $excludefolder_regex = ('(?i)^(' + (($script:excludeFolder | ForEach {[regex]::escape($_)}) -join "|") + ')$') -replace "\\\*", ".*"
    $excludedc_regex = ('(?i)^(' + (($script:excludeDC | ForEach {[regex]::escape($_)}) -join "|") + ')$') -replace "\\\*", ".*"
   
    $outputary = @() 
    $vcenterobj = Get-VBRServer -Name $vcenter
    $vms=@{}

    # Build a hash table of all VMs.  Key is either Job Object Id (for any VM ever in a Veeam job) or vCenter ID+MoRef
    # Assume unprotected (!), and populate Cluster, DataCenter, and Name fields for hash key value
    Find-VBRViEntity -Server $vcenterobj | 
        Where-Object {$_.Type -eq "Vm" -and $_.VmFolderName -notmatch $excludefolder_regex} |
        Where-Object {$_.Name -notmatch $excludevms_regex} |
        Where-Object {$_.Path.Split("\")[1] -notmatch $excludedc_regex} |
        ForEach {$vms.Add(($_.FindObject().Id, $_.Id -ne $null)[0], @("!", $_.Path.Split("\")[1], $_.Path.Split("\")[2], $_.Name))}

    Find-VBRViEntity -VMsandTemplates -Server $vcenterobj |
        Where-Object {$_.Type -eq "Vm" -and $_.IsTemplate -eq "True" -and $_.VmFolderName -notmatch $excludefolder_regex} |
        Where-Object {$_.Name -notmatch $excludevms_regex} |
        Where-Object {$_.Path.Split("\")[1] -notmatch $excludedc_regex} |
        ForEach {$vms.Add(($_.FindObject().Id, $_.Id -ne $null)[0], @("!", $_.Path.Split("\")[1], $_.VmHostName, $_.Name))}
      
    # Find all backup task sessions that have ended in the last x hours and not ending in Failure
    $vbrtasksessions = (Get-VBRBackupSession | 
        Where-Object {$_.JobType -eq "Backup" -and $_.EndTime -ge (Get-Date).addhours(-$script:HourstoCheck)}) |
        Get-VBRTaskSession | Where-Object {$_.Status -ne "Failed"}

    # Compare VM list to session list and update found VMs status to "Protected"
    If ($vbrtasksessions) {
      Foreach ($vmtask in $vbrtasksessions) {
         If($vms.ContainsKey($vmtask.Info.ObjectId)) {
            $vms[$vmtask.Info.ObjectId][0]=$vmtask.JobName
         }
      }
   }
   $vms.GetEnumerator() | Sort-Object Value
}
Thanks again!!
tsightler
VP, Product Management
Posts: 6035
Liked: 2860 times
Joined: Jun 05, 2009 12:57 pm
Full Name: Tom Sightler
Contact:

Re: My Veeam Report turns 2.0!

Post by tsightler »

I'm still thinking about it. I feel like there has to be a better way, but it hasn't come to me yet. I did consider the possibility of moving the Find-VBRViEntity commands outside of the function and just run it twice for the entire report, rather than twice for each vCenter.
Backerupper
Enthusiast
Posts: 47
Liked: 24 times
Joined: Dec 28, 2012 2:32 pm
Contact:

Re: My Veeam Report turns 2.0!

Post by Backerupper »

Excellent point - Not sure if there ever was a real need, but there doesn't appear to be one now to run separately on each vCenter

Code: Select all

Function Get-VMsBackupStatus {
# Convert exclusion list to simple regular expression
    $excludevms_regex = ('(?i)^(' + (($script:excludeVMs | ForEach {[regex]::escape($_)}) -join "|") + ')$') -replace "\\\*", ".*"
    $excludefolder_regex = ('(?i)^(' + (($script:excludeFolder | ForEach {[regex]::escape($_)}) -join "|") + ')$') -replace "\\\*", ".*"
    $excludedc_regex = ('(?i)^(' + (($script:excludeDC | ForEach {[regex]::escape($_)}) -join "|") + ')$') -replace "\\\*", ".*"
   
    $vms=@{}

    # Build a hash table of all VMs.  Key is either Job Object Id (for any VM ever in a Veeam job) or vCenter ID+MoRef
    # Assume unprotected (!), and populate Cluster, DataCenter, and Name fields for hash key value
    Find-VBRViEntity | 
        Where-Object {$_.Type -eq "Vm" -and $_.VmFolderName -notmatch $excludefolder_regex} |
        Where-Object {$_.Name -notmatch $excludevms_regex} |
        Where-Object {$_.Path.Split("\")[1] -notmatch $excludedc_regex} |
        ForEach {$vms.Add(($_.FindObject().Id, $_.Id -ne $null)[0], @("!", $_.Path.Split("\")[1], $_.Path.Split("\")[2], $_.Name))}

Find-VBRViEntity -VMsandTemplates |
    Where-Object {$_.Type -eq "Vm" -and $_.IsTemplate -eq "True" -and $_.VmFolderName -notmatch $excludefolder_regex} |
    Where-Object {$_.Name -notmatch $excludevms_regex} |
    Where-Object {$_.Path.Split("\")[1] -notmatch $excludedc_regex} |
    ForEach {$vms.Add(($_.FindObject().Id, $_.Id -ne $null)[0], @("!", $_.Path.Split("\")[1], $_.VmHostName, $_.Name))}
      
    # Find all backup task sessions that have ended in the last x hours and not ending in Failure
    $vbrtasksessions = (Get-VBRBackupSession | 
        Where-Object {$_.JobType -eq "Backup" -and $_.EndTime -ge (Get-Date).addhours(-$script:HourstoCheck)}) |
        Get-VBRTaskSession | Where-Object {$_.Status -ne "Failed"}

    # Compare VM list to session list and update found VMs status to "Protected"
    If ($vbrtasksessions) {
      Foreach ($vmtask in $vbrtasksessions) {
         If($vms.ContainsKey($vmtask.Info.ObjectId)) {
            $vms[$vmtask.Info.ObjectId][0]=$vmtask.JobName
         }
      }
   }
   $vms.GetEnumerator() | Sort-Object Value
}
Post Reply

Who is online

Users browsing this forum: No registered users and 18 guests