PVS V4.1 Documentation Script Has Been Updated 2-FEB-2014

February 2, 2014

PowerShell, PVS

I received a message on Twitter from the very smart and always helpful Shane Kleinert (@ShaneKleinert you should follow him) asking if my Provisioning Services (PVS) documentation script had the vDisk Versions and Audit Trail information. I was sure it did until I checked and saw that I had never added that information to my script and no one, until now, had noticed or asked about it. Of course, I had to add those two items to my script. This article describes the process.

I downloaded the latest PVS 7.1 PowerShell Programmers Guide and started looking for where to find the proper MCLI-GET command to use. I finally found it in MCLI-Get DiskVersion. The only problem was that it requires three parameters and my PVSObject function can only handle one parameter. Parameters passed to the MCLI-Get wrapper are comma delimited (with no spaces allowed) and PowerShell then treats that as an array. So I had to put the code to handle getting DiskVersion directly into the script since I couldn’t find a way around the comma delimited parameters treated as an array issue.

There are two items I have yet to find in the PVS PowerShell Programmers Guide: Boot production devices from version and which version is the current booting version (the one with the green checkmark).

vDisk Versions

vDisk Versions

If you know how to get those two pieces of information, please let me know and I will add them to the script and give you credit.

The code to handle vDisk Versions:

#process Versions menu
#get versions info
Write-Verbose "$(Get-Date): `t`t`tProcessing vDisk Versions"
$VersionsObjects = @()
$error.Clear()
$MCLIGetResult = Mcli-Get DiskVersion -p diskLocatorName="$($Disk.diskLocatorName)",storeName="$($disk.storeName)",siteName="$($disk.siteName)"
If($error.Count -eq 0)
{
	#build versions object
	$PluralObject = @()
	$SingleObject = $Null
	ForEach($record in $MCLIGetResult)
	{
		If($record.length -gt 5 -and $record.substring(0,6) -eq "Record")
		{
			If($SingleObject -ne $Null)
			{
				$PluralObject += $SingleObject
			}
			$SingleObject = new-object System.Object
		}

		$index = $record.IndexOf(':')
		If($index -gt 0)
		{
			$property = $record.SubString(0, $index)
			$value    = $record.SubString($index + 2)
			If($property -ne "Executing")
			{
				Add-Member -inputObject $SingleObject -MemberType NoteProperty -Name $property -Value $value
			}
		}
	}
	$PluralObject += $SingleObject
	$DiskVersions = $PluralObject
	
	If($DiskVersions -ne $Null)
	{
		WriteWordLine 0 1 "vDisk Versions"
		ForEach($DiskVersion in $DiskVersions)
		{
			Write-Verbose "$(Get-Date): `t`t`t`tProcessing vDisk Version $($DiskVersion.version)"
			WriteWordLine 0 2 "Version`t`t`t`t`t: " $DiskVersion.version
			WriteWordLine 0 2 "Created`t`t`t`t`t: " $DiskVersion.createDate
			If(![String]::IsNullOrEmpty($DiskVersion.scheduledDate))
			{
				WriteWordLine 0 2 "Released`t`t`t`t: " $DiskVersion.scheduledDate
			}
			WriteWordLine 0 2 "Devices`t`t`t`t`t: " $DiskVersion.deviceCount
			WriteWordLine 0 2 "Access`t`t`t`t`t: " -NoNewLine
			Switch ($DiskVersion.access)
			{
				0 {WriteWordLine 0 0 "Production"}
				1 {WriteWordLine 0 0 "Maintenance"}
				2 {WriteWordLine 0 0 "Maintenance Highest Version"}
				3 {WriteWordLine 0 0 "Override"}
				4 {WriteWordLine 0 0 "Merge"}
				5 {WriteWordLine 0 0 "Merge Maintenance"}
				6 {WriteWordLine 0 0 "Merge Test"}
				7 {WriteWordLine 0 0 "Test"}
				Default {WriteWordLine 0 0 "Access could not be determined: $($DiskVersion.access)"}
			}
			WriteWordLine 0 2 "Type`t`t`t`t`t: " -NoNewLine
			Switch ($DiskVersion.type)
			{
				0 {WriteWordLine 0 0 "Base"}
				1 {WriteWordLine 0 0 "Manual"}
				2 {WriteWordLine 0 0 "Automatic"}
				3 {WriteWordLine 0 0 "Merge"}
				4 {WriteWordLine 0 0 "Merge Base"}
				Default {WriteWordLine 0 0 "Type could not be determined: $($DiskVersion.type)"}
			}
			If(![String]::IsNullOrEmpty($DiskVersion.description))
			{
				WriteWordLine 0 2 "Properties`t`t`t`t: " $DiskVersion.description
			}
			WriteWordLine 0 2 "Can Delete`t`t`t`t: "  -NoNewLine
			Switch ($DiskVersion.canDelete)
			{
				0 {WriteWordLine 0 0 "No"}
				1 {WriteWordLine 0 0 "Yes"}
			}
			WriteWordLine 0 2 "Can Merge`t`t`t`t: "  -NoNewLine
			Switch ($DiskVersion.canMerge)
			{
				0 {WriteWordLine 0 0 "No"}
				1 {WriteWordLine 0 0 "Yes"}
			}
			WriteWordLine 0 2 "Can Merge Base`t`t`t: "  -NoNewLine
			Switch ($DiskVersion.canMergeBase)
			{
				0 {WriteWordLine 0 0 "No"}
				1 {WriteWordLine 0 0 "Yes"}
			}
			WriteWordLine 0 2 "Can Promote`t`t`t`t: "  -NoNewLine
			Switch ($DiskVersion.canPromote)
			{
				0 {WriteWordLine 0 0 "No"}
				1 {WriteWordLine 0 0 "Yes"}
			}
			WriteWordLine 0 2 "Can Revert back to Test`t`t`t: "  -NoNewLine
			Switch ($DiskVersion.canRevertTest)
			{
				0 {WriteWordLine 0 0 "No"}
				1 {WriteWordLine 0 0 "Yes"}
			}
			WriteWordLine 0 2 "Can Revert back to Maintenance`t: "  -NoNewLine
			Switch ($DiskVersion.canRevertMaintenance)
			{
				0 {WriteWordLine 0 0 "No"}
				1 {WriteWordLine 0 0 "Yes"}
			}
			WriteWordLine 0 2 "Can Set Scheduled Date`t`t`t: "  -NoNewLine
			Switch ($DiskVersion.canSetScheduledDate)
			{
				0 {WriteWordLine 0 0 "No"}
				1 {WriteWordLine 0 0 "Yes"}
			}
			WriteWordLine 0 2 "Can Override`t`t`t`t: "  -NoNewLine
			Switch ($DiskVersion.canOverride)
			{
				0 {WriteWordLine 0 0 "No"}
				1 {WriteWordLine 0 0 "Yes"}
			}
			WriteWordLine 0 2 "Is Pending`t`t`t`t: "  -NoNewLine
			Switch ($DiskVersion.isPending)
			{
				0 {WriteWordLine 0 0 "No, version Scheduled Date has occurred"}
				1 {WriteWordLine 0 0 "Yes, version Scheduled Date has not occurred"}
			}
			WriteWordLine 0 2 "Replication Status`t`t`t: " -NoNewLine
			Switch ($DiskVersion.goodInventoryStatus)
			{
				0 {WriteWordLine 0 0 "Not available on all servers"}
				1 {WriteWordLine 0 0 "Available on all servers"}
				Default {WriteWordLine 0 0 "Replication status could not be determined: $($DiskVersion.goodInventoryStatus)"}
			}
			WriteWordLine 0 2 "Disk Filename`t`t`t`t: " $DiskVersion.diskFileName
			WriteWordLine 0 0 ""
		}
	}
}
Else
{
	WriteWordLine 0 0 "Disk Version information could not be retrieved"
	WriteWordLine 0 0 "Error returned is " $error[0].FullyQualifiedErrorId.Split(',')[0].Trim()
}

Here is what the vDisk Versions part of the report looks like for my lab.

vDisk Versions Report

vDisk Versions Report

Next up was getting the Audit Trail data.

Audit Trail

Audit Trail

This is retrieved using MCLI-GET AuditTrail. This also required three parameters with two of them being Start and End dates. That required me adding two more parameters to my script: StartDate and EndDate. The dates are entered in MM/DD/YYYY format but MCLI-GET AuditTrail requires the dates in YYYY/MM/DD format. The default, by both MCLI-GET AuditTrail and the script, is to include only the previous seven days of audit trail data.

The audit trail data is put into a Word table to get more data per page. In order for the table to fit in the width of the page I removed the Number and Domain columns and changed the font size to 9 points.

In the PVS console, you get to the Audit Trail report at the Farm level but MCLI-GET AuditTrail does not have a way to retrieve the data for the Farm??? I decided to put it as the last item, on a new page, in the Site section of the report.

The new parameters:

.PARAMETER StartDate
	Start date, in MM/DD/YYYY format, for the Audit Trail report.
	Default is today's date minus seven days.
.PARAMETER EndDate
	End date, in MM/DD/YYYY format, for the Audit Trail report.
	Default is today's date.


.EXAMPLE
	PS C:\PSScript > .\PVS_Inventory_V41.ps1 -StartDate "01/01/2014" -EndDate "01/31/2014" -verbose
	
	Will use all Default values.
	HKEY_CURRENT_USER\Software\Microsoft\Office\Common\UserInfo\Company="Carl Webster"
	$env:username = Administrator
	AdminAddress = LocalHost

	Carl Webster for the Company Name.
	Sideline for the Cover Page format.
	Administrator for the User Name.
	LocalHost for AdminAddress.
	Will display verbose messages as the script is running.
	Will return all Audit Trail entries from "01/01/2014" through "01/31/2014".


	[parameter(
	Position = 9, 
	Mandatory=$False)
	] 
	[Datetime]$StartDate = ((Get-Date -displayhint date).AddDays(-7)),

	[parameter(
	Position = 10, 
	Mandatory=$False)
	] 
	[Datetime]$EndDate = (Get-Date -displayhint date)

The code to handle the audit trail data is 300 lines so I will snip out some lines for this article.

#add Audit Trail
Write-Verbose "$(Get-Date): `t`t`tProcessing Audit Trail"
$AuditTrailObjects = @()
$error.Clear()

#the audittrail call requires the dates in YYYY/MM/DD format
$Sdate = '{0:yyyy/MM/dd}' -f $StartDate
$Edate = '{0:yyyy/MM/dd}' -f $EndDate
$MCLIGetResult = Mcli-Get AuditTrail -p siteName="$($PVSSite.siteName)",beginDate="$($SDate)",endDate="$($EDate)"
If($error.Count -eq 0)
{
	#build audit trail object
	$PluralObject = @()
	$SingleObject = $Null
	ForEach($record in $MCLIGetResult)
	{
		If($record.length -gt 5 -and $record.substring(0,6) -eq "Record")
		{
			If($SingleObject -ne $Null)
			{
				$PluralObject += $SingleObject
			}
			$SingleObject = new-object System.Object
		}

		$index = $record.IndexOf(':')
		If($index -gt 0)
		{
			$property = $record.SubString(0, $index)
			$value    = $record.SubString($index + 2)
			If($property -ne "Executing")
			{
				Add-Member -inputObject $SingleObject -MemberType NoteProperty -Name $property -Value $value
			}
		}
	}
	$PluralObject += $SingleObject
	$Audits = $PluralObject
	
	If($Audits -ne $Null)
	{
		$selection.InsertNewPage()
		WriteWordLine 2 0 "Audit Trail"
		WriteWordLine 0 0 "Audit Trail for dates $($StartDate) through $($EndDate)"
		$TableRange   = $doc.Application.Selection.Range
		[int]$Columns = 6
		If($Audits -is [array])
		{
			[int]$Rows = $Audits.Count +1
		}
		Else
		{
			[int]$Rows = 2
		}
		Write-Verbose "$(Get-Date): `t`t`t`tAdd Audit Trail table to doc"
		$Table = $doc.Tables.Add($TableRange, $Rows, $Columns)
		$table.Style = "Table Grid"
		$table.Borders.InsideLineStyle = 0
		$table.Borders.OutsideLineStyle = 1
		$Table.Cell(1,1).Shading.BackgroundPatternColor = $wdColorGray15
		$Table.Cell(1,1).Range.Font.Bold = $True
		$Table.Cell(1,1).Range.Font.size = 9
		$Table.Cell(1,1).Range.Text = "Date/Time"
		$Table.Cell(1,2).Shading.BackgroundPatternColor = $wdColorGray15
		$Table.Cell(1,2).Range.Font.Bold = $True
		$Table.Cell(1,2).Range.Font.size = 9
		$Table.Cell(1,2).Range.Text = "Action"
		$Table.Cell(1,3).Shading.BackgroundPatternColor = $wdColorGray15
		$Table.Cell(1,3).Range.Font.Bold = $True
		$Table.Cell(1,3).Range.Font.size = 9
		$Table.Cell(1,3).Range.Text = "Type"
		$Table.Cell(1,4).Shading.BackgroundPatternColor = $wdColorGray15
		$Table.Cell(1,4).Range.Font.Bold = $True
		$Table.Cell(1,4).Range.Font.size = 9
		$Table.Cell(1,4).Range.Text = "Name"
		$Table.Cell(1,5).Shading.BackgroundPatternColor = $wdColorGray15
		$Table.Cell(1,5).Range.Font.Bold = $True
		$Table.Cell(1,5).Range.Font.size = 9
		$Table.Cell(1,5).Range.Text = "User"
		$Table.Cell(1,6).Shading.BackgroundPatternColor = $wdColorGray15
		$Table.Cell(1,6).Range.Font.Bold = $True
		$Table.Cell(1,6).Range.Font.size = 9
		$Table.Cell(1,6).Range.Text = "Path"
		[int]$xRow = 1
		[int]$Cnt = 0
		ForEach($Audit in $Audits)
		{
			$xRow++
			$Cnt++
			Write-Verbose "$(Get-Date): `t`t`tAdding row for audit trail item # $Cnt"
			If($xRow % 2 -eq 0)
			{
				$Table.Cell($xRow,1).Shading.BackgroundPatternColor = $wdColorGray05
				$Table.Cell($xRow,2).Shading.BackgroundPatternColor = $wdColorGray05
				$Table.Cell($xRow,3).Shading.BackgroundPatternColor = $wdColorGray05
				$Table.Cell($xRow,4).Shading.BackgroundPatternColor = $wdColorGray05
				$Table.Cell($xRow,5).Shading.BackgroundPatternColor = $wdColorGray05
				$Table.Cell($xRow,6).Shading.BackgroundPatternColor = $wdColorGray05
			}
			$Table.Cell($xRow,1).Range.Font.size = 9
			$Table.Cell($xRow,1).Range.Text = $Audit.time
			$Tmp = ""
			Switch([int]$Audit.action)
			{
				1 { $Tmp = "AddAuthGroup"}
				2 { $Tmp = "AddCollection"}
				3 { $Tmp = "AddDevice"}
				4 { $Tmp = "AddDiskLocator"}
				5 { $Tmp = "AddFarmView"}
				6 { $Tmp = "AddServer"}
				7 { $Tmp = "AddSite"}
				8 { $Tmp = "AddSiteView"}
				9 { $Tmp = "AddStore"}
				10 { $Tmp = "AddUserGroup"}
				# SNIP 140 lines of code
				7033 { $Tmp = "SetListUserGroupCustomPropertyAdd"}				
			}
			$Table.Cell($xRow,2).Range.Font.size = 9
			$Table.Cell($xRow,2).Range.Text = $Tmp
			$Tmp = ""
			Switch ($Audit.type)
			{
				0 {$Tmp = "Many"}
				1 {$Tmp = "AuthGroup"}
				2 {$Tmp = "Collection"}
				3 {$Tmp = "Device"}
				4 {$Tmp = "Disk"}
				5 {$Tmp = "DeskLocator"}
				6 {$Tmp = "Farm"}
				7 {$Tmp = "FarmView"}
				8 {$Tmp = "Server"}
				9 {$Tmp = "Site"}
				10 {$Tmp = "SiteView"}
				11 {$Tmp = "Store"}
				12 {$Tmp = "System"}
				13 {$Tmp = "UserGroup"}
				Default { {$Tmp = "Undefined"}}
			}
			$Table.Cell($xRow,3).Range.Font.size = 9
			$Table.Cell($xRow,3).Range.Text = $Tmp
			$Table.Cell($xRow,4).Range.Font.size = 9
			$Table.Cell($xRow,4).Range.Text = $Audit.objectName
			$Table.Cell($xRow,5).Range.Font.size = 9
			$Table.Cell($xRow,5).Range.Text = $Audit.userName
			$Table.Cell($xRow,6).Range.Font.size = 9
			$Table.Cell($xRow,6).Range.Text = $Audit.path
		}
		$table.AutoFitBehavior(1)

		#return focus back to document
		Write-Verbose "$(Get-Date): `t`tReturn focus back to document"
		$doc.ActiveWindow.ActivePane.view.SeekView=$wdSeekMainDocument

		#move to the end of the current document
		Write-Verbose "$(Get-Date): `tMove to the end of the current document"
		$selection.EndKey($wdStory,$wdMove) | Out-Null
		Write-Verbose "$(Get-Date):"
	}
}

The Audit Trail report for my lab.

Audit Trail Report

Audit Trail Report

Script version is now 4.12. And thanks to Shane, the script is now 500 lines longer! Man that was a lot of late night typing. 🙂

NOTE: This script is continually updated. You can always find the most current version by going to http://carlwebster.com/where-to-get-copies-of-the-documentation-scripts/

Thanks

Webster

, , ,

About Carl Webster

Webster is a Sr. Solutions Architect for Choice Solutions, LLC and specializes in Citrix, Active Directory and Technical Documentation. Webster has been working with Citrix products for many years starting with Multi-User OS/2 in 1990.

View all posts by Carl Webster

No comments yet.

Leave a Reply