<#
.Synopsis
Get the newest recovery points and the schedule on the local core or on a list of cores and set a warning threshold to easily differentiate between valid recovery points and those which need attention.
.Description
Script that may be delivered via e-mail which determines the newest recovery points on one or multiple cores and the schedule and sets a warning threshold to easily differentiate between valid recovery points and those which need attention.
.Parameter hoursthreshold
default 24 hours (value in hours) -- sets the warning label allowing to differentiate between recovery points that are OK and those that need attention.
.Parameter interactive
allows generating the report for the desired protected agents by selecting them from a gridview; the gridview shows once for each core; cancelling it results in exiting the script
.Parameter reportpath
default "<userprofile>\Downloads\SnapReport.html"
allows setting up the reports location. To reduce administrative effort, for reports sent via e-mail, the recommended path should be located in an accessible folder (i.e. c:\reports).
The name of the report is modified to include the date and a guid for proper identification. If your e-mail server removes javascript from html files, generate the report with a .txt extension and change it to html after receiving it.
.PARAMETER corenames
mandatory -- the name of the core(s) to run the report on or the path to file containing cores information
.PARAMETER username
the username for the credentials to be used. if missing a dialogbox will pop up
.PARAMETER password
the password for the credentials to be used. it MUST be encoded using the encoding function of the script. if missing a dialogbox will pop up
.PARAMETER mail
sends the report via e-mail using the e-mail configuration section at the end of the script (needs to be configured running the script with the -setupemail parameter) instead of opening it on the local machine. The report may be sent, by default, via gmail but the values may be changed to match a different e-mail server.
.PARAMETER encodepassword
allows encoding a clear text password. all passwords MUST be encoded. this parameter is exclusive to any other parameters.
.PARAMETER createcorefile
opens Notepad and sticks in the table header for the CSV file containing the cores.
.PARAMETER setupemail
configures the e-mail settings in the script; a new script with the e-mail values is generated in the same directory with the original script. The original script is left unaltered as reference for future use.
.Example
.\transfer.7.ps1 -hoursthreshold 8
.Example
.\transfer.7.ps1 -reportpath c:\reports\myreport.html -mail
.Example
.\transfer.7.ps1 -setupemail
.Example
.\transfer.7.ps1 -mail -corenames c:\temp\mycores.csv
.Example
.\transfer.7.ps1 -corenames Core2012r2-0,Core2012r2-1 -username .\administrator -password cgAzAHMAdAByAGkAYwB0ADMAZAAhACEAR -reportpath c:\myfolder\myfile.html
.Example
.\transfer.7.ps1 -corenames c:\temp\mycores.csv -username .\administrator -password cgAzAHMAdAByAGkAYwB0ADMAZAAhACEAR
#>
param ( $hoursthreshold=24,
$reportpath="$($env:userprofile)\Downloads\SnapReport.html",
$corenames=$null,
$username=$null,
$password=$null,
[switch]$encodepassword,
[switch]$createcorefile,
[switch]$interactive,
[switch]$mail,
[switch]$setupemail
)
function get-infoobject{
param ($cred=$null,$corename=$env:computername,$uriend)
try{
if(!($corename)){$corename=$env:computername}
$url = "https://$($corename):8006/apprecovery/api/core/"
$url = $url + $uriend
$wc = New-Object System.Net.WebClient
if($cred){$wc.Credentials = $cred}else{$wc.usedefaultcredentials=$true}
[System.Net.ServicePointManager]::ServerCertificateValidationCallback = { $true }
[xml]$objectinfo = $wc.DownloadString($url)
}catch{Write-Host "Unable to retrieve requested object from core $corename" -f red;return $null }
return $objectinfo
}
function parse-phpdate{
param($phpdate)
$option = [System.StringSplitOptions]::RemoveEmptyEntries
$isodate=$null
#$AllSeparators="P","Y","M","D","T","H","M","S"
#$AllSeparators0="Y","M","D"
#$AllSeparators1="H","M","S"
[array]$allhashseparators=@{"Y"="years";"M"="months";"D"="days"},@{"H"="hours";"M"="minutes";"S"="seconds"}
[array]$AllSeparators = $allhashseparators[0].keys,$allhashseparators[1].keys
[array]$phpdatesplit = ($phpdate.replace("P","")).split("T")
for($k=0; $k -lt $phpdatesplit.count; $k++){
$delem = $phpdatesplit[$k]
if($delem){
[array]$separators=$null
foreach($s in $AllSeparators[$k]){
if($delem.indexof($s) -ne -1){$separators += $s}
}
$sortdirection="";
if($k -eq 0){$sortdirection =" -descending"}
$sb = [ScriptBlock]::Create("sort-object $sortdirection")
$separators = $separators | Invoke-Command -ScriptBlock $sb
[array]$mytime = $delem.split($separators, $option)
for($i=0;$i -lt $mytime.count; $i++){
$isodate += "$($mytime[$i]) $($allhashseparators[$k][$separators[$i]]) "
}
}
}
return $isodate
}
function reparse-date{
#runs for hh,mm,ss only
param($myparsedtime)
if($myparsedtime.indexof("seconds") -eq -1){$myparsedtime = $myparsedtime + " 0 seconds"}
if($myparsedtime.indexof("hours") -eq -1){$myparsedtime = "0 hours" + $myparsedtime}
if($myparsedtime.indexof("minutes") -eq -1){$myparsedtime = $myparsedtime.replace("hours","hours 0 minutes")}
$myparsedtime=$myparsedtime.Replace("hours",":")
$myparsedtime=$myparsedtime.Replace("minutes",":")
$myparsedtime=$myparsedtime.Replace("seconds","")
$myparsedtime=$myparsedtime.Replace(" ","")
return (get-date($myparsedtime)).ToString("hh:mm tt")
}
function reportpath{
param($reportpath)
$uniqueid=[Guid]::NewGuid()
$path= split-path -path $reportpath -Parent
if(!(Test-Path $path)){
write-host "`nPath does not exist. Attempting to create..." -f Yellow
New-Item -Path $path -ItemType directory}
$file = split-path -path $reportpath -leaf
$filename = [System.IO.Path]::GetFileNameWithoutExtension($file)
$fileextension=[System.IO.Path]::GetExtension($file)
$reportpath="$($path)\$($filename)_$((get-date).tostring(`"yyyy-MM-dd`"))-$($uniqueid)$($fileextension)";
return $reportpath
}
function pageheader {
$htmlhead = @"
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<style>
BODY{background-color:lightgray;margin:10px 10px 0 10px;font-family:verdana,arial,sans-serif;font-size:12px}
TABLE{border-width: 1px;border-style: solid;border-color: grey;border-collapse: collapse;width:100%;margin: 0 0 0 0;}
TH{border-width: 1px;padding: 10px;border-style: solid;border-color: black;background-color:lightblue}
TD{border-width: 1px;padding: 10px;border-style: solid;border-color: black;background-color:white;}a{font-weight:bold;}
</style>
<script type="text/javascript">
<!--
function toggle_visibility(id) {
var e = document.getElementsByName(id);
var id0=id+"0";
var q = document.getElementById(id0);
for (k=0; k < e.length; k++){
if(e[k].style.display == 'block'){
e[k].style.display = 'none';
}
else{
e[k].style.display = 'block';
}
}
if(e[0].style.display == 'block'){q.value = "Collapse"}
else{q.value="Expand"}
}
//-->
</script>
</head><body>
"@
return $htmlhead
}
function converthtml{
param([pscustomobject]$table)
if($table){ return $table | ConvertTo-Html -Fragment | out-string }
return $null
}
function autochangevalues {
param ($scriptpath)
Write-Host @"
This Option allows modifying the e-mail settings without editing the script.
The result is saved in a new script so, if at some point new e-mail settings are to be used or if something goes wrong it is easy to
revert to the default settings.
Please note that you need to enter the actual values.
The admissible values for the `$usecredentials variable are yes or no.
The admissible values for the `$usessl variable are yes or no.
If `$usecredentials is set to no, the `$username and `$password values do not matter and are excluded from the dialog.
If `$usecredentials is set to yes, the `$password variable may be entered in clear text (you do not need to encode it prior entering it).
However, it is masked to the user (password type of entry) and encoded at script level
The list of values to configure is shown below:
"@ -f Yellow
$x = @(get-content $scriptpath)
[array]$emailsettings = '$mailfrom=','$mailto=','smtpusername=','smtppassword=','smtpserver=','smtpport=','$usecredentials=','$enableSSL='
$scriptsize = $x.count
$Changeblock=@()
for($k=0;$k -lt $scriptsize;$k++)
{
for ($i=0;$i -lt $emailsettings.count;$i++)
{
if ($x[$k]){$line=$x[$k]}else {continue}
if ($line.IndexOf($emailsettings[$i]) -ne -1)
{
$temp=$line.split("=")
$obj = [pscustomobject]@{pos=$k;var=$temp[0].trim();value=$temp[1].trim()}
if($obj.var[0] -eq "`$"){
$changeblock += $obj
}
}
}
}
$changeblock | ft -Property var,value -AutoSize
do{
Write-Host "`n`nE-mail Setup Values`n-------------------" -f Yellow
Foreach ($changeblockline in $Changeblock){
if($changeblockline.var -like "*smtppassword*"){
$replacement = read-host "Enter new value for $($changeblockline.var)" -AsSecureString
$replacement = [Runtime.InteropServices.Marshal]::PtrToStringAuto([Runtime.InteropServices.Marshal]::SecureStringToBSTR( $replacement ))
$replacement = [convert]::ToBase64String([Text.Encoding]::UNICODE.GetBytes($replacement))
}else
{
$replacement = read-host "Enter new value for $($changeblockline.var) `[current value: $($changeblockline.value) <Enter> to keep it`]"
}
if(!($replacement)){$replacement = $changeblockline.value}else{$replacement="`"$($replacement)`""}
$x[$changeblockline.pos] = "$($changeblockline.var)=$replacement"
Write-Host "$($changeblockline.var) set to: $($x[$changeblockline.pos])" -f Yellow
if(($changeblockline.var -like "*usecredentials*") -and ($replacement -like "*n*")){Write-Host "Skipping credentials" -f Yellow;break}
}
$answer=$null
do{
$answer = Read-Host "Are the entered values OK? [Yes/No]"
}until("yes","no" -contains $answer)
}until ($answer -like "Y*")
$x | out-file -FilePath (modifyscriptpath -initialscriptpath $scriptpath)
}
function modifyscriptpath{
param ($initialscriptpath)
$path= split-path -path $initialscriptpath -Parent
$file = split-path -path $initialscriptpath -leaf
$filename = [System.IO.Path]::GetFileNameWithoutExtension($file)
$fileextension=[System.IO.Path]::GetExtension($file)
$scriptpath="$($path)\$($filename)_$((get-date).tostring(`"yyyy-MM-dd`")).email$($fileextension)";
Write-Host "`r`nNew script path is $scriptpath `r`n" -f Green
return $scriptpath
}
function decode-password {
param($encodedpassword)
$password =@([System.Text.Encoding]::UNICODE.GetString([System.Convert]::FromBase64String($encodedpassword)))
return $password
}
function encode-password{
param($cleartextpassword=$(throw "got to pass something"))
cls
Write-Host @"
Encode Password Feature
-----------------------
This feature allows encoding passwords so they are not stored in clear text.
This is NOT similar to encrypting a password but just a way of avoiding exposing passwords to unauthorized eyes.
Please note that the passwords are proceesed during the script execution as secured stings and embedded in a credential object.
Passwords are NOT sent to the core in clear text!
"@ -f Green
Write-Host "---------------" -f White
if($cleartextpassword){$encodedstring = [convert]::ToBase64String([Text.Encoding]::UNICODE.GetBytes($cleartextpassword))}
else{
do{
$securestring = Read-Host "Please Enter the Password to Encode (it will be masked)" -AsSecureString
$tempstring = [Runtime.InteropServices.Marshal]::PtrToStringAuto([Runtime.InteropServices.Marshal]::SecureStringToBSTR( $securestring))
$securestring2= Read-Host "Please Re-enter the Password to Encode (it will be masked)" -AsSecureString
$tempstring2 = [Runtime.InteropServices.Marshal]::PtrToStringAuto([Runtime.InteropServices.Marshal]::SecureStringToBSTR( $securestring2))
}until ($tempstring -eq $tempstring2)
$tempstring = [Runtime.InteropServices.Marshal]::PtrToStringAuto([Runtime.InteropServices.Marshal]::SecureStringToBSTR( $securestring))
$encodedstring = [convert]::ToBase64String([Text.Encoding]::UNICODE.GetBytes($tempstring))
}
$null | clip.exe
$encodedstring | clip.exe
Write-Host "`r`nThe Encoded password length is $($encodedstring.Length) unicode characters`r`n"
Write-Host "The encoded password is:`r`n`r`n$encodedstring`r`n`r`nIt has already been copied to the clipboard.`r`nPlease paste it on the script command line as the -password parameter"
#try{ C:\Users\stefan_popescu\Documents\EncodePasswordnew.ps1 -cleartextpassword }catch{ C:\Users\stefan_popescu\Documents\EncodePasswordnew.ps1 -cleartextpassword $null}
#exit
}
function parse-coresparam{
param ([array]$coresx=$null)
Write-Host "Value(s) for the corenames parameter:"
$coresx
if($coresx -eq $null){write-Host "No cores value entered. Trying Local Machine...";$coresx=$env:computername}
$corexobject=@()
if(Test-Path -path $coresx[0]){
$corexobject = Import-Csv $coresx
}
else{
if($coresx.Count -eq 1 -and $coresx[0].IndexOf("\") -ne -1){Write-Host "Path not found. Exiting";exit}
foreach ($corex in $coresx){
$corexline = [pscustomobject]@{corename=$corex.trim();username=$null;password=$null}
$corexobject += $corexline
}
}
return $corexobject
}
function create-corefile{
Write-Host @"
This function is meant to assist you creating a file containing the cores, user names and passwords to be used for generating the reports.
The format is pretty simple: corename, username, encodedpassword.
Avoid spaces, they may create unforseen issues.
To encode a password please use the option:
.\CondensedEvents.remote.5.ps1 -encodedpassword <cleartextpassword>
You will need them to prepare this file or the command line
If some of the cores have the same username and password, use the -username and -password parameters (specifying the encoded password)
All cores without specified credentials in the file will fallback the username and password in the command line.
For instance, the file will look as follows:
corename, username, encodedpassword
<core1>,user1,sdfasdfsdfsdfasdf
<core2>
<core3>,user2,fghdfgdghdghfghgh
If no credentials are found for a specific core and the default credentials are not present, a popup window will prompt you to enter your credentials.
If for some reason, you do not see the table header in notepad, please rightclick and paste.
Do not forget to save.
"@ -ForegroundColor Green
$null | clip.exe
"corename,username,password" | clip.exe
#start notepad, get process object (to get pid later)
$process = Start-Process -PassThru notepad;
# activate Notepad window
# based on http://stackoverflow.com/a/4994020/1030702
# SW_SHOW activates and shows a window http://msdn.microsoft.com/en-us/library/windows/desktop/ms633548%28v=vs.85%29.aspx
$SW_SHOW = 5;
$sig = '[DllImport("user32.dll")] public static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);';
Add-Type -MemberDefinition $sig -name NativeMethods -namespace Win32;
[Win32.NativeMethods]::ShowWindow($process.Id, $SW_SHOW) | Out-Null;
# send a "Ctrl+V" keystroke to the active window
# from http://stackoverflow.com/a/17851491/1030702
Add-Type -AssemblyName System.Windows.Forms;
[System.Windows.Forms.SendKeys]::SendWait('^V');
start-sleep 1
}
function convert-bytes {
param($x)
if ($x/1TB -lt 1){
if ($x/1GB -lt 1){
if ($x/1MB -lt 1){
if ($x/1KB -lt 1){
{return [pscustomobject]@{value=($x);UM="B"}}
}
else {return [pscustomobject]@{value=($x/1KB);UM="KB"}}
}
else {return [pscustomobject]@{value=($x/1MB);UM="MB"}}
}
else {return [pscustomobject]@{value=($x/1GB);UM="GB"}}
}
else {return [pscustomobject]@{value=($x/1TB);UM="TB"}}
}
function finish-convert {
param ($y)
return "{0:N2}$($y.um)" -f $y.value
}
function simplecore-status{
param($computername,$credx)
$acs="unreachable";
$amd="unreachable";
#ping
$x = test-connection $computername -ErrorAction SilentlyContinue -Count 1
if($x){$ping="Yes"}else{$ping="No"}
try{
if($credx){
$y=get-wmiobject win32_service -computer $computername -Credential $credx -filter "Name = 'AppAssurecore' or Name = 'AppAssuremongod'" -ErrorAction Stop
}
else{
$y=get-wmiobject win32_service -computer $computername -filter "Name = 'AppAssurecore' or Name = 'AppAssuremongod'" -ErrorAction Stop
}
}catch{
$FailedItem = $_.Exception.ItemName; Write-Host $FailedItem -ForegroundColor Red
}
if($y){
$acs = ($y | where {$_.name -eq "AppAssurecore"}).state
$amd=($y | where {$_.name -eq "AppAssuremongod"}).state
}
#$testline =[pscustomobject]@{Ping=$ping;AppAssureCoreService=$acs;AppAssureMongod=$amd}
$testline=@"
<strong>
<table style="border-style:hidden; width:120px" >
<tr><td style="border-style:hidden;vertical-align:top;padding:0,0,0,0">Ping:</td><td style="border-style:hidden;vertical-align:top;padding:0,0,0,0">$($ping)</td></tr>
<tr><td style="border-style:hidden;vertical-align:top;padding:0,0,0,0">Core Service:</td><td style="border-style:hidden;vertical-align:top;padding:0,0,0,0">$($acs)</td></tr>
<tr><td style="border-style:hidden;vertical-align:top;padding:0,0,0,0">Mongod Service:</td><td style="border-style:hidden;vertical-align:top;padding:0,0,0,0">$($amd)</td></tr>
</table></strong>
"@
return $testline
}
function private:get-coremetadata{
param($corename=$env:computername,$cred=$null)
$cinfo=$null
$x = get-infoobject -uriend "status/coreSystemInfo" -corename $corename -cred $cred
if ($x){
$corevolumes=$x.coresysteminfo.coresummarymetadata.volumes.volume.displayname | sort-object
$cvolumes=$null
foreach($corevolume in $corevolumes){
$cvolumes+=$corevolume + "<br>"
}
$psobject=$x.coresysteminfo.coresummarymetadata | select-object hostname,osversion,@{n="memory";e={"{0:N2}GB" -f (($_.physicalMemory )/1gb)}},@{n="volumes";e={$cvolumes}}
$cinfo = $null
$anatomy = $psobject.psobject.properties
foreach ($element in $anatomy){$cinfo += "<tr><td style=`"border-style:hidden;vertical-align:top;padding:0,0,0,0`">$($element.Name):</td><td style=`"border-style:hidden;vertical-align:top;padding:0,0,0,0`">$($element.Value)</td></tr>"}
$cinfo += "<tr><td colspan=`"2`" style=`"border-style:hidden;vertical-align:top;padding:0,0,0,0`">$(get-coreretentionpolicy)</td></tr>"
$cinfo = "<table style=`"border-style:hidden;padding:0,0,0,0;width:100px;`" >" + $cinfo + "</table>"
}
else{$cinfo = simplecore-status -computername $corename -credx $cred}
return $cinfo
}
function get-retentioncategory{
param ($elem)
if($elem._x003C_Enabled_x003E_k__BackingField -eq "false"){$elem._x003C_Quantity_x003E_k__BackingField = "0"}
#$result = [pscustomobject]@{enabled=$elem._x003C_Enabled_x003E_k__BackingField;quantity=$elem._x003C_Quantity_x003E_k__BackingField ;um=$elem._x003C_Unit_x003E_k__BackingField};
$result= "$($elem._x003C_Quantity_x003E_k__BackingField) $($elem._x003C_Unit_x003E_k__BackingField)"
return $result
}
function get-coreretentionpolicy{
$rc = (get-infoobject -uriend "rollup/config" -corename $corename -cred $cred).rollupServiceConfig
$z = $rc.defaultRetentionpolicy
$retpol=@"
<table>
<th colspan="2">Core Retention Policy:</td></th>
<tr><td>All for:</td><td>$(get-retentioncategory -elem $z.keepAlls)</td></tr>
<tr><td>One/Hour:</td><td>$(get-retentioncategory -elem $z.keepOnePerHour)</td></tr>
<tr><td>One/Day:</td><td>$(get-retentioncategory -elem $z.keepOnePerDay)</td></tr>
<tr><td>One/Week:</td><td>$(get-retentioncategory -elem $z.keepOnePerWeek)</td></tr>
<tr><td>One/Month:</td><td>$(get-retentioncategory -elem $z.keepOnePerMonth)</td></tr>
<tr><td>One/Year:</td><td>$(get-retentioncategory -elem $z.keepOnePerYear)</td></tr>
</table>
"@
return $retpol
}
function get-retentionpolicy{
param($agentid)
$rc = (get-infoobject -uriend "rollup/agents/$agentId" -corename $corename -cred $cred).agentretentionpolicy
$z = $rc.policy
$retpol=@"
<table><th colspan="2">Retention Policy</th>
<tr><td>Use Core Policy:</td><td>$($rc.usedefaultpolicy)</td></tr>
"@
if($rc.usedefaultpolicy -eq "false"){
$retpol +=@"
<tr><td>All for:</td><td>$(get-retentioncategory -elem $z.keepAlls)</td></tr>
<tr><td>One/Hour:</td><td>$(get-retentioncategory -elem $z.keepOnePerHour)</td></tr>
<tr><td>One/Day:</td><td>$(get-retentioncategory -elem $z.keepOnePerDay)</td></tr>
<tr><td>One/Week:</td><td>$(get-retentioncategory -elem $z.keepOnePerWeek)</td></tr>
<tr><td>One/Month:</td><td>$(get-retentioncategory -elem $z.keepOnePerMonth)</td></tr>
<tr><td>One/Year:</td><td>$(get-retentioncategory -elem $z.keepOnePerYear)</td></tr>
"@
}
$retpol += "</table>"
return $retpol
}
function private:get-agenttransferevents{
param ($corename=$env:computername,$coreport="8006",$currentdate=(get-date),$hourstreshold=24,$agentid, $cred=$null)
$dt=$currentdate.ToUniversalTime();
$start=($dt.addhours(-1*$hourstreshold)).tostring("yyyy-MM-ddTHH:mm:ss")
$end=($dt).tostring("yyyy-MM-ddTHH:mm:ss")
$body=@"
<?xml version="1.0" ?>
<agentReportRequest xmlns="http://apprecovery.com/management/api/2010/05">
<endTime>$end</endTime>
<startTime>$start</startTime>
<agentId>$($agentid)</agentId>
</agentReportRequest>
"@
$uriend = "report/agentReport"
if(!($cred)){$eventssummary = Invoke-RestMethod -Uri "https://$($corename):$($coreport)/apprecovery/api/core/$($uriend)" -Method "Put" -Body $body -UseDefaultCredentials -ContentType "application/xml"}
else {$eventssummary = Invoke-RestMethod -Uri "https://$($corename):$($coreport)/apprecovery/api/core/$($uriend)" -Method "Put" -Body $body -Credential $cred -ContentType "application/xml"}
$parsedevents = $eventssummary.report.items.reportItem | where {$_.jobtype -eq "TransferJob"} | sort-object $_.starttime -Descending | select-object agent,startTime,status,@{n="errordetails";e={$_.errordetails}},@{n="errormessage";e={$_.errormessage}}
return $parsedevents
}
#----- Main Script Starts Here -------------------------------------
$powersh = $psversiontable
if($powersh.psversion.Major -ge 3){Write-host "Powershell Version $($powersh.psversion.Major).$($powersh.psversion.Minor) is supported!" -f Yellow}
else {Write-Host "Powershell must be v3.0 or later"}
$reportpath = reportpath -reportpath $reportpath
$lt = [GUID]::NewGuid()
$gt = [GUID]::NewGuid()
$br = [GUID]::NewGuid()
#setup email
if($setupemail.IsPresent){
autochangevalues -scriptpath $MyInvocation.InvocationName
exit
}
#encode a password if called with the appropriate argument
if($encodepassword.ispresent){
encode-password -cleartextpassword $null
exit
}
#create file with cores
if($createcorefile.IsPresent){
create-corefile
exit
}
$currentdate = get-date
[array]$permissibleagettype="Protected","Cluster","Clusternode"
[array]$daysoftheweek="monday","tuesday","wednesday","thursday","friday","saturday","sunday"
#Begin building Report
$title = "Appassure Snap At-A-Glance Report:<br/> Generated on $($currentdate.ToString(`"ddd MM/dd/yyyy hh:mm tt`"))<br>Warning threshold: $hoursthreshold hours"
$h = pageheader
$h += "<table><tr><td><h2>$($title)</h2></td></tr></table><br/>"
[array]$coresobject = $null
#parse core names
$coresobject = parse-coresparam -coresx $corenames
$coresobject
$summary=$null
foreach($object in $coresobject){
if(!($object.corename)){"Null core object name...";continue}
$summary+="<a href=#top$($object.corename)>$($object.corename)</a> "
}
$h +="<br><a name=`"topcore`"></a><table><tr><th>Cores List</th></tr><tr><td>$summary</td></tr></table>$cr"
#start main cycle
foreach($object in $coresobject){
if(!($object.corename)){"Null core object name...";continue}
#parse credentials
[string]$corename = $object.corename
if($object.username){$usernamex=$object.username.trim()}else{$usernamex=$username}
if($object.password){$passwordx = $object.password.trim()}else{$passwordx=$password}
Write-Host "`nWorking with: $corename $usernamex $passwordx" -ForegroundColor Yellow
#credentials
if($corename -eq $env:computername -and $usernamex -eq $null -and $passwordx -eq $null){
Write-Host "Running Report with current credentials for the local core only" -f Yellow
}else{
if($usernamex -eq $null -or $passwordx -eq $null){
$cred=get-credential -username $usernamex -message "Enter $corename credentials"
if(!($cred)){"Cancelled by user...";exit
}
}
else{
$decodedpassword = decode-password -encodedpassword $passwordx
$secPw = ConvertTo-SecureString $decodedpassword -AsPlainText -Force
$cred = New-Object PSCredential -ArgumentList $usernamex,$secPw
}
}
$scs=$null
$scs = get-coremetadata -corename $corename -cred $cred
$h += "<br><table style=`"border:1px solid;`"><tr><td style=`"vertical-align:top;padding:0,0,0,0;border:0`"><a name=`"top$($object.corename)`"></a><a href=#topcore>$($object.corename)</a><br><br><strong>$($scs)</strong></td><td style=`"border:0`">"
#repositories-----------------------------------------------------
$repositoryinformation=$null
$eventsInformation=$null
$repositories=$null
try{
#Repositories
$repoinfo = get-infoobject -uriend "reposManagement/repositories" -corename $corename -cred $cred
$repoobject = $repoinfo.repositories.repository | sort-object -Property repositoryname -Descending
#process repositories data
[array]$repositories=$null
foreach($reposingle in $repoobject){
$size=finish-convert -y (convert-bytes -x $reposingle.size)
$free=finish-convert -y (convert-bytes -x $reposingle.free);
$percentfree="{0:N2}%" -f (100*([long]($reposingle.free))/([long]($reposingle.size)))
$compression="{0:N2}%" -f (100*($reposingle.stats.compressionratio))
$dedupe="{0:N2}%" -f (([long]($reposingle.stats.dedupehits))/([long]($reposingle.stats.dedupehits) + [long]($reposingle.stats.dedupemisses) + 1)*100)
$repoline = [pscustomobject]@{Name=$reposingle.repositoryname;Size=$size;Free=$free;PercentFree=$percentfree;Compression=$compression;Deduplication=$dedupe}
$repositories+=$repoline
}
$repositories = $repositories | sort-object -property Name | select-object -Property @{n="Repository Name";e={$_.name}},@{n="Repository Size";e={$_.size}},@{n="Free Space";e={$_.free}},@{n="Free Space `%";e={$_.percentfree}},@{n="Compression Rate";e={$_.compression}},@{n="Deduplication Rate";e={$_.deduplication}}
} catch {Write-Host "Retrieving repository information from $corename failed" -f Red;$repositoryInformation="unavailable";$repositories=$null;$repoobject=$null}
#-----------------------------------------------------------------
if($repositories){$repotable = $repositories | ConvertTo-Html -Fragment | Out-String}else{$repotable=$null}
$h +="<br>" + $repotable;
#agents per core
$agentsall = get-infoobject -uriend "agents" -corename $corename -cred $cred
$agents = $agentsall.agents.agent | where {$permissibleagettype -contains $_.agenttype} | select-object displayname,agenttype,Status,id | sort-object displayname
if($interactive.ispresent){$agents=$agents | Out-GridView -PassThru
if(!($agents)){Write-Host "Action Cancelled... Exiting" -f Yellow;exit}}
$agentanchors=$null
if($agents){
foreach ($aaa in $agents){
$agentitem = @"
<a href=#id$($aaa.id)>$($aaa.displayname)</a>
"@
$agentanchors += "$($agentitem) ";
}
$h += "<a name=top></a><br><table><tr><th>Agents</th></tr><tr><td>$agentanchors</td></tr></table><br>"
foreach($agent in $agents){
Write-Host "`n`nAgent: $($agent.displayname)" -ForegroundColor Yellow
$javabutton = "<input type='button' id=`"id0-$($agent.id)0`" onclick=`"toggle_visibility(`'id0-$($agent.id)`')`" value='Expand'/>"
$spanstart=@"
<a name="id0-$($agent.id)" style="display:none">
"@
$spanend="</a>"
$h += @"
<table style="border-width: 1px;"><tr><td style="vertical-align:top;border-style:none hidden none none ;">
<strong>
<table style="border-style:hidden;padding:0,0,0,0;border-collapse:collapse"><tr>
<td width="20px" style="border-style:hidden;vertical-align:top;padding:0,0,0,0">Agent:</td>
<td style="border-style:hidden;vertical-align:top;padding:0,0,0,0"><a name=id$($agent.id)></a><a href=#top>$($agent.displayname)</a></td></tr>
<tr style="border-style:hidden "><td style="border-style:hidden;vertical-align:top;padding:0,0,0,0">Schedule:</td>
<td style="border-style:hidden;vertical-align:top;padding:0,0,0,0">$($javabutton)</td></tr>
<td style="border-style:hidden vertical-align:top;padding:0,0,0,0">Type:</td>
<td style="border-style:hidden;vertical-align:top;padding:0,0,0,0">$($agent.agenttype)</td></tr>
<tr><td style="border-style:hidden;vertical-align:top;padding:0,0,0,0">Status:</td>
<td style="border-style:hidden;vertical-align:top;padding:0,0,0,0">$($agent.status)</td></tr>
<tr></strong>
<td colspan="2" style="border-style:hidden;vertical-align:top;padding:0,0,0,0">$($spanstart)$(get-retentionpolicy -agentid $agent.id)$($spanend)</td></tr></table>
</td><td style="vertical-align:top;border-style:none none none none ;">
"@
$xferconfig = get-infoobject -uriend "xfer/schedule/$($agent.id)/actualProtectionConfiguration" -corename $corename -cred $cred
$transferresult = get-agenttransferevents -corename $corename -cred $cred -agentid $agent.id -hourstreshold $hoursthreshold -currentdate $currentdate
#protectiongroups
if(!($xferconfig)){$h += "</td></tr></table><br>";continue}
$protectiongroups=$xferconfig.protectionConfig.pgs.pgConfig | sort-object -Property $_.volumenames.agentvolume
$j=0;
foreach($pg in $protectiongroups){
$Warning = $null
$lastsnapraw=$pg.lastsnap.nil
if($lastsnapraw -ne "true"){
$lastsnap = "$(((get-date($pg.lastsnap | where {$_.nil -ne "true"})).tolocaltime().tostring()))"
$nextsnap = "$(((get-date($pg.nextsnap | where {$_.nil -ne "true"})).tolocaltime().tostring()))"
$lastsnapresult = $transferresult | where {(get-date($_.startTime)).tostring("yyyy-MM-ddTHH:mm:ss") -eq (get-date($pg.Lastsnap)).tostring("yyyy-MM-ddTHH:mm:ss")}
$howlongago = New-TimeSpan -Start (get-date($lastsnap)) -End $currentdate
$howlongtowait = New-TimeSpan -End (get-date($nextsnap)) -start $currentdate
if ($howlongago.TotalHours -gt $hoursthreshold){$warning = "$($lt)strong$($gt)$($lt)font color=red$($gt)Warning!$($lt)/font$($gt)$($lt)/strong$($gt)"}
else{if($lastsnapresult.status-eq "Succeeded"){$warning = "$($lt)strong$($gt)$($lt)font color=green$($gt)OK$($lt)/font$($gt)$($lt)/strong$($gt)"}
else{$warning = "$($lt)strong$($gt)$($lt)font color=red$($gt)Warning![partial failure]$($lt)/font$($gt)$($lt)/strong$($gt)"}
}
[array]$howlongagostringx = $howlongago.ToString().split(".")}
if($howlongagostringx.count -gt 2){$howlongagostring = $howlongagostringx[0] + " days " + $howlongagostringx[1]}else {$howlongagostring= $howlongagostringx[0]}
if($howlongtowait){$howlongtowaitstring = $howlongtowait.ToString().split(".")[0]}else{$howlongtowaitstring=$null}
[string]$volumes=$null
foreach($volume in $pg.volumeNames.agentVolume.displayname){
$volumes += "$($volume)$br"
}
$statusline=$null
$statusline=[pscustomobject]@{Status=$Warning;ProtectedVolumes=$volumes;LastSnap="$($lt)strong$($gt)$($lastsnap)$($lt)/strong$($gt)";HowLongAgo=$howlongagostring;Threshold="$hoursthreshold hours";NextSnap=$nextsnap;HowLongFromNow=$howlongtowaitstring}
if($statusline){$h += "<strong>Protection Group: $($j), Id: $($pg.id)</strong>" + (converthtml -table $statusline);$h +="<br>"}
$xschedule=@()
foreach($day in ($daysoftheweek[4..5])){$superday=$null
if($day -eq "friday"){$superday = "Monday to Friday"} else {$superday="Saturday and Sunday"}
$schedule = $pg.transferScheduleConfiguration.$($day).transferScheduleItemsCollection.transferScheduleItem |sort-object -property priority -Descending | select-object Period,@{n="StartTime";e={reparse-date -myparsedtime (parse-phpdate -phpdate $_.starttime)}},@{n="EndTime";e={reparse-date -myparsedtime (parse-phpdate -phpdate $_.endtime)}},@{n="Interval";e={(parse-phpdate -phpdate $_.step)}}
if($schedule){$schedule[0].Period = $superday;$xschedule +=$schedule}
}
if($xschedule){$h += $spanstart +(converthtml -table $xschedule) + $spanend;$h += "<br>"}
$j++;
}
$h += "</td></tr></table><br>"
}
}
$h += "</td></tr></table><br>"
}
$h.replace($lt,"<").replace($gt,">").replace($singlequote,"'").replace($br,"<br>") | Out-File -FilePath $reportpath
if($mail.IsPresent){
#E-Mail Configuration Section (Gmail)
#------------------------------------
#Start Configuration
###################################
$mailfrom="gmailuser@gmail.com"
$mailto="recipient@yourcompany.com"
$smtpserver="smtp.gmail.com"
$smtpport="587"
$enableSSL="yes"
#values for $usecredentials and $enableSSL are $true and $false
$usecredentials="yes"
$smtpusername="gmailuser"
$smtppassword="gmailpassword"
###################################
#End Configuration
#--------------------
$mailsubject="AppAssure Core `"$($env:computername)`" Snap At-A-Glance Report"
$mailbody = "Please find attached AppAssure $($env:computername) Core At-A-Glance Report generated on $((get-date).tostring())"
$attachment = new-object Net.Mail.Attachment($reportpath)
$message = new-object Net.Mail.MailMessage
$message.from = $mailfrom
$message.to.Add($mailto)
$message.Subject=$mailsubject
$message.body=$mailbody
$message.Attachments.Add($attachment)
$smtpclient = new-object Net.Mail.SmtpClient($smtpServer, $smtpport)
if($usecredentials -like "y*"){$smtpclient.credentials=New-Object System.Net.NetworkCredential($smtpusername,(decode-password -encodedpassword $smtppassword)); Write-Host "checkpointCredentials" }
if($enableSSL -like "y*"){$smtpclient.EnableSsl=$true;Write-Host "checkpointSSL"}
$smtpclient.send($message)
Write-Host "Done!"
}
else{
Invoke-Item -Path $reportpath
}