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).

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:
[powershell]
#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()
}
[/powershell]
Here is what the vDisk Versions part of the report looks like for my lab.

Next up was getting the Audit Trail data.

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:
[powershell]
.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)
[/powershell]
The code to handle the audit trail data is 300 lines so I will snip out some lines for this article.
[powershell]
#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):"
}
}
[/powershell]
The Audit Trail report for my lab.

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 https://carlwebster.com/where-to-get-copies-of-the-documentation-scripts/
Thanks
Webster