Merge-EvidenceLocks.ps1
function Write-Info { Param ( [Parameter()] [string] $Message ) process { Write-Information "$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss') - $Message" } } <# .SYNOPSIS Merges all duplicate evidence locks into a single record .DESCRIPTION Duplicate evidence locks are identified by creating a key out of the Header, StartTime and EndTime values. If the -CreateNew flag is not provided, then the information about the unique, deduplicated evidence locks will be logged and the cmdlet will return. If -CreateNew is provided, then for each group of evidence locks, this cmdlet creates a list of all devices associated with the group, identifies the oldest duplicate record in the group, as well as the record with the evidence lock expiration furthest into the future. And a new evidence lock entry is created with these properties. If -DeleteOnSuccess is provided, then if there were NO ERRORS when creating the new evidence lock entry, the old evidence lock entries will be deleted, leaving only the new consolidated entry. Note: If errors occur, it's possible the new evidence lock could be partially created. If it wasn't 100% successful, then it is assumed to be a complete failure and deletions will not occur, and the failure will be logged to the Failures*.csv file. .PARAMETER LogDirectory Specifies the path where up to three CSV files will be saved. These CSV files will be named with a timestamp to avoid overwriting the results of prior executions. UniqueEvidenceLocks*.csv - After grouping duplicate evidence locks into sets, each unique set will be documented here Successes*.csv - If -CreateNew is provided, any 100% successfully created evidence locks will be documented here Failures*.csv - If -CreateNew is provided, any evidence locks that were not 100% successfully created will be documented here .PARAMETER CreateNew Attempt to create a new merged evidence lock for each set of duplicates. .PARAMETER DeleteOnSuccess Delete original evidence lock entries if the new consolidated evidence lock was created without errors. .PARAMETER RebuildSingleEntries Create new evidence lock entries even for evidence locks with no duplicates. This may be useful if it is believed the Recording Servers no longer posess a record of the original evidence lock definition. Using this parameter, a new evidence lock will be created, and successfully creating the evidence lock means the Recording Server has acknowledged that is has received and added the "sticker" to the media database. .EXAMPLE Connect-ManagementServer -Server hostname Merge-EvidenceLocks Generates a report of unique evidence locks including the largest "Size" property from each unique set for reference .EXAMPLE Connect-ManagementServer -Server hostname Merge-EvidenceLocks -CreateNew Attempt to create new consolidated evidence locks, but do not remove any existing evidence locks even when the new lock is created successfully .EXAMPLE Connect-ManagementServer -Server hostname Merge-EvidenceLocks -CreateNew -DeleteOnSuccess -RebuildSingleEntries Attempt a full de-duplication of evidence locks, as well as rebuilding evidence lock entries that do not currently have any duplicates. #> function Merge-EvidenceLocks { Param ( [Parameter()] [string] $LogDirectory = '.\', [Parameter()] [switch] $CreateNew, [Parameter()] [switch] $DeleteOnSuccess, [Parameter()] [switch] $RebuildSingleEntries ) begin { try { # Verify already connected to Management Server and LogDirectory path is writable. Get-ManagementServer | Out-Null $tmpFile = "tmp$(Get-Date -Format 'yyyyMMddHHmmss').txt" New-Item -Path ( Join-Path -Path $LogDirectory -ChildPath $tmpFile ) -ErrorAction Stop | Remove-Item } catch { throw } } process { Write-Info "Retrieving all evidence locks for later sorting. . ." $allRecords = Get-EvidenceLock Write-Info "Found $($allRecords.Count) evidence locks" if ($allRecords.Count -eq 0) { return } Write-Info "Sorting evidence locks into groups by Header, Start and End times. . ." $groups = $allRecords | Group-Object -Property { [string]::Join(',', @($_.Header, $_.StartTime.Ticks.ToString(), $_.EndTime.Ticks.ToString())) } $uniqueEvidenceLocks = @() $successes = @() $failures = @() foreach ($group in $groups) { Write-Info "Processing '$($group.Name)'" $deviceIds = New-Object System.Collections.ArrayList $originalLock = $group.Group[0] $expire = $group.Group[0].RetentionExpire $size = $group.Group[0].RetentionSize foreach ($lock in $group.Group) { # Build a list of all unique device IDs found in all duplicate locks in the group foreach ($deviceId in $lock.DeviceIds) { if (!$deviceIds.Contains($deviceId)) { $deviceIds.Add($deviceId) | Out-Null; } } # Ensure we grab the first lock of the group ever created # And if some of the expire dates are different lets play # it safe and use the expiration date furthest in the future if ($lock.Created -lt $originalLock.Created) { $originalLock = $lock } if ($lock.RetentionExpire -gt $expire) { $expire = $lock.RetentionExpire } if ($lock.RetentionSize -gt $size) { $size = $lock.RetentionSize } } # Save information about this deduplicated lock to pscustomobject $row = New-Object psobject $row | Add-Member -MemberType NoteProperty -Name Header -Value ([System.Security.SecurityElement]::Escape($originalLock.Header)) $row | Add-Member -MemberType NoteProperty -Name Created -Value $originalLock.Created.ToString('yyyy-MM-ddTHH:mm:ss.fffZ') $row | Add-Member -MemberType NoteProperty -Name StartTime -Value $originalLock.StartTime.ToString('yyyy-MM-ddTHH:mm:ss.fffZ') $row | Add-Member -MemberType NoteProperty -Name EndTime -Value $originalLock.EndTime.ToString('yyyy-MM-ddTHH:mm:ss.fffZ') $row | Add-Member -MemberType NoteProperty -Name Expires -Value $expire.ToString('yyyy-MM-ddTHH:mm:ss.fffZ') $row | Add-Member -MemberType NoteProperty -Name DeviceIds -Value ([string]::Join(',', $deviceIds.ToArray())) $row | Add-Member -MemberType NoteProperty -Name Size -Value $size $uniqueEvidenceLocks += $row if (!$CreateNew) { Write-Info 'The -CreateNew flag was not supplied, so deduplication will not proceed' continue } if ($group.Count -eq 1 -and !$RebuildSingleEntries) { Write-Info 'This evidence lock has no duplicates and the -RebuildSingleEntries flag is not present. Skipping to the next group.' continue } # Begin deduplication $magicDate = get-date -Year 2084 try { $originalLock.Description = "OriginalUser=$($originalLock.User)`r`n$($originalLock.Description)" if ((Get-Date) -gt $expire) { $originalLock.Description = "OriginalExpiration=$($expire)`r`n$($originalLock.Description)" $expire = $magicDate } Write-Info "Creating new Evidence Lock for '$($originalLock.Header)'" $result = Add-EvidenceLock -Header $originalLock.Header -Description $originalLock.Description -DeviceIds $deviceIds -FootageFrom $originalLock.StartTime -FootageTo $originalLock.EndTime -ExpireDate $expire -RetentionType UserDefined -ErrorAction SilentlyContinue if ($result.Status -ne [VideoOS.Common.Proxy.Server.WCF.ResultStatus]::Success) { foreach ($fault in $result.FaultDevices) { Write-Warning "$($result.Status): $($fault.Message.Trim()), Device: $($fault.DeviceId)" } Write-Error "Add-EvidenceLock result for '$($originalLock.Header)': $($result.Status)" -ErrorAction Stop } $successes += $row Write-Info "Successfully created Evidence Lock with ID $($result.MarkedData.Id)" # No errors creating lock, delete duplicates if (!$DeleteOnSuccess) { Write-Info 'The -DeleteOnSuccess flag was not supplied, so the new lock is created but the old locks will remain.' continue } foreach ($lock in $group.Group) { Write-Info "Deleting duplicate originally created on $($lock.Created)" $lock | Remove-EvidenceLock } } catch { $ErrorMessage = $_.Exception.Message $failures += $row Write-Error $ErrorMessage } } $timestamp = Get-Date -Format 'yyyy-MM-dd_HH-mm-ss' $filePath = Join-Path -Path $LogDirectory -ChildPath "UniqueEvidenceLocks_$timestamp.csv" Write-Info "Saving deduplicated information to $filePath. . ." $uniqueEvidenceLocks | Export-Csv -Path $filePath -NoTypeInformation if ($CreateNew) { if ($successes.Count -gt 0) { $filePath = Join-Path -Path $LogDirectory -ChildPath "Successes_$timestamp.csv" Write-Info "Saving successfully created evidence lock information to $filePath. . ." $successes | Export-Csv -Path $filePath -NoTypeInformation } if ($failures.Count -gt 0) { $filePath = Join-Path -Path $LogDirectory -ChildPath "Failures_$timestamp.csv" Write-Info "Saving failed deduplications to $filePath. . ." $failures | Export-Csv -Path $filePath -NoTypeInformation } } } } |