# Copyright (c) Laserfiche. <# .SYNOPSIS Runs a saved Audit Trail report and exports the result to a file. .DESCRIPTION This is a sample script. It runs a saved report on a repository or Forms audit data source, and exports the result to the specified file and path. The exported file must be on the machine where Audit Trail is installed. By default IIS will terminate the application when there are no active user for 20 minutes, so when exporting large amount of data, consider using the `-Raw` mode, or change the `Idle timeout` in IIS (this is not 100% safe, since the AppPool will still recycle on an interval or schedule). When exporting data for other applications to consume, use the -Raw flag. .PARAMETER DataSource The repository or Forms data source to run the report against. You can get the value from the "dataSource=" URL parameter value when you select the data source on the main /AuditTrail reporting page or open the saved report in a browser. Repository data sources must include both the RepositoryName and ServerName in "RepoName(ServerName)" format. In the main page URL, the value will have parenthesis characters around the ServerName like "TestRepository(localhost)". E.g.,http://localhost/AuditTrail/?lang=en&dataSource=TestRepository(localhost) In the report page URL, the value will have URL-encoded parenthesis characters around the ServerName like "TestRepository%28localhost%29". E.g., http://localhost/AuditTrail/reports/sKXtXgGe1k6tFxEBNcS_yAlang=en&dataSource=TestRepository%28localhost%29 Both non-encoded and encoded formats are acceptable, as they are used as inputs for a URI string. To avoid PowerShell syntax errors on the () characters, non-encoded repository data source values must be surrounded in single or double quotes, like: -DataSource 'TestRepository(localhost)' Forms data sources are a GUID string. Forms data source GUIDs do not have parenthesis () so there is only one format. E.g., where the URLs are: Main page URL: http://localhost/AuditTrail/?lang=en&dataSource=63ca4d94-3b98-4314-817f-ad75f17e1795 Report page URL: http://localhost/AuditTrail/reports/y72mbzN710WpZMkK-RmAqg?lang=en&dataSource=63ca4d94-3b98-4314-817f-ad75f17e1795&view=table The Forms data source is '63ca4d94-3b98-4314-817f-ad75f17e1795' .PARAMETER Repository LEGACY PARAMETER. Left for compatibility reasons. Use the "DataSource" parameter instead. Please see the "DataSource" parameter description's "Repository data sources" section for details. .PARAMETER ReportId ID of the saved report. The ReportId is the segment appearing after /reports/ in URL when open a saved report. E.g., when opening a saved report with http://localhost/AuditTrail/reports/sKXtXgGe1k6tFxEBNcS_yA?lang=en&dataSource=TestRepository%28localhost%29, then the ReportId for the saved report is 'sKXtXgGe1k6tFxEBNcS_yA'. .PARAMETER ExportFile Target file, on the machine where Audit Trail is installed, to save the report. If the file already exists, it will be deleted first, then the new file will be saved with the same name. Supported file types are '.xlsx' (Excel) and '.csv' (TAB DELIMITED csv, not comma delimited). If no file type is specified, e.g., "savedReport", '.xlsx' is used by default, unless the export has over 100k rows, in which case '.csv' is automatically used due to technical limitations generating large xlsx files. Explicitly using '.csv' is recommended for most use cases. E.g., "savedReport.csv". By default, the export file is saved under '%ProgramData%\Laserfiche\AuditAnalytics\Export' on the server hosting Audit Trail Reporting. To change the export directory: 1. Update the Audit Trail web.config file as detailed below 2. Grant the "IIS AppPool\AuditTrailAppPool" local user access rights to the new export location. The export file is saved under the directory specified in the '' setting in the '' section of the '.\Program Files\Laserfiche\Audit Trail\WebAuditReport\web.config' file. If no ExportFolder value is specified, the default ExportFolder directory is '%ProgramData%\Laserfiche\AuditAnalytics\Export'. If you want a different directory, please update the '' setting in this 'web.config' file. It is a good practice to create a backup copy of the web.config file before making any changes. UNC paths to other servers or file shares are not supported. If you need the export file to end up at a remote location, either write a secondary script or make a copy of this script and extend it to copy/move the export file to the desired location once the export is complete (you can use the "ReturnValue" outputs to check for completion). This parameter takes a full path, e.g., "C:\AuditReportExports\savedReport.csv", which must be under the "ExportFolder" (including subdirectories). Audit Trail will attempt to create a specified subdirectory if it does not exist. For example, with the default ExportFolder (no value set in the web.config), this parameter value will create a subfolder named "2024-06-20" and save the export file as "savedReport.csv" within it: -ExportFile 'C:\ProgramData\Laserfiche\AuditAnalytics\Export\2024-06-20\savedReport.csv' If you specify a path that is not under the ExportFolder, you will receive an error message in PowerShell like: "'C:\\AuditReportExports\\savedReport.csv' is not a valid exporting path, please using a path under 'C:\\ProgramData\\Laserfiche\\AuditAnalytics\\Export', and the permission settings are correct. If you want another directory to export, please contact Administrator to change the settings of Audit Trail web site. .PARAMETER Raw (Optional) Export audit report in raw data mode. This mode is designed for exporting extremely large amount of data (e.g., exporting more than 1M audit events). By using this option, it can avoid the exporting process being terminated unexpectedly after hours of running. Please note the audit events will be exported as '.csv' file format only and shown as the raw plain values indexed by Laserfiche Full-Text Search Engine. Raw data mode is recommended when exporting data for other applications to use. .PARAMETER BaseUrl (Optional) Url to Audit Trail Reporting Site. By default "http://localhost/AuditTrail" is used. .PARAMETER Username (Optional) If not provided, uses the current Windows account, e.g., [DomainName]\[AccountName]. Make sure the account has permission to access the report on AuditTrail web site. See: https://doc.laserfiche.com/laserfiche.documentation/11/administration/en-us/Default.htm#../Subsystems/LFAdmin/Content/Audit_Permissions.htm .PARAMETER Password (Optional) Password for the specified Windows account. .PARAMETER Language (Optional) Target language for the exported content. By default 'en' is used. .PARAMETER TimeZone (Optional) TimeZone used for the datetime field in the exported content, by default use the timezone saved in the saved report or the timezone of the server. It is using the CLDR Timezone name used by the Windows system. Run "tzutil /l" in Cmd or PowerShell to get the most current list of time zones. See: https://learn.microsoft.com/en-us/windows-hardware/manufacture/desktop/default-time-zones E.g., "Pacific Standard Time", "US Mountain Standard Time" .PARAMETER CheckResultInMinutes (Optional) Check report status after specified minutes. By default, -1 is used. The behavior with this default value depends on if the RecheckResultUntilComplete switch is set or not. If RecheckResultUntilComplete is not set: The script will check the export status once almost immedidately (after 5s), then complete. If RecheckResultUntilComplete is set: To avoid re-checking export status as fast as the script loop can execute requests, automatically sets the value to 1 (minute). When set to a value 1 or above: If RecheckResultUntilComplete is not set: The script will check the export status once status once almost immedidately (after 5s) to catch any quick completions, then once more after the specified number of minutes, and complete. If RecheckResultUntilComplete is set: The script will check the export status once status once almost immedidately (after 5s) to catch any quick completions, then recheck after the specified number of minutes until it returns a status code that indicates the export job has completed (finished, failed, or canceled). .PARAMETER RecheckResultUntilComplete (Optional) Recheck the export status until completed. See the "CheckResultInMinutes" parameter description for more details. .PARAMETER ReturnValue (Optional) Sets script return output type. Valid values are 'Token', 'StateNum', 'StateNumAsString', or 'StateString'. Default is 'Token' Token (System.String) Returns a token string which can be used to retrieve the progress of the export with a web request like: `Invoke-RestMethod -Method Get -Uri "http://localhost/AuditTrail/api/SavedReport/export/$token/status" -UseDefaultCredentials` or `Invoke-RestMethod -Method Get -Uri "http://localhost/AuditTrail/api/SavedReport/raw/$token/status" -UseDefaultCredentials` when exporting in '-Raw' mode. You can also use the token returned to cancel a running expor with a web request like: `Invoke-RestMethod -Method Delete -Uri "http://localhost/AuditTrail/api/SavedReport/export/$token/cancel" -UseDefaultCredentials` or `Invoke-RestMethod -Method Delete -Uri "http://localhost/AuditTrail/api/SavedReport/raw/$token/cancel` when exporting in '-Raw' mode. StateNum (System.Int32), StateNumAsString (System.String) or StateString (System.String): Returns the export status "state" value in either numeric or string form. The result state string values and their corresponding numeric codes are listed below. 0 - 'Not Started' 1 - 'Exporting' 2 - 'Exported' 5 - 'Failed' 6 - 'Canceled' E.g., for a successful export: -ReturnValue 'StateNum' : 2 -ReturnValue 'StateNumAsString' : "2" -ReturnValue 'StateString' : "Exported" .PARAMETER AdditionalSources (Optional) Additional repository or Forms data sources to run the report on. Use this option to export from multiple repositories or Forms instances and combine the exported data into a single file. If you want each repository or Forms instance to have a separate output file, do not use this option and instead run the script once per repository or Forms instance. Repository and Forms data sources cannot be combined, so do not specify a repository for DataSource and Forms for AdditionalSources, or the other way around. Remarks: When this option is used, only the '.csv' file type is supported. DOES NOT INCLUDE DATA SOURCE AS A COLUMN. .PARAMETER AdditionalRepositories (Optional) LEGACY PARAMETER. Left for compatibility reasons. Use the "AdditionalSources" parameter instead. Please see the "AdditionalSources" parameter description for details. Remarks: When this option is used, only the '.csv' file type is supported. .INPUTS None. You cannot pipe objects to ExportReport.ps1 .OUTPUTS The script output is determined by the "ReturnValue" parameter. Please see its parameter description for details. .EXAMPLE .\Export-AuditReport.ps1 -DataSource 'TestRepository(localhost)' -ReportId sKXtXgGe1k6tFxEBNcS_yA -ExportFile 'C:\ProgramData\Laserfiche\AuditAnalytics\Export\2024-06-20\savedReport.csv' .EXAMPLE $password = "mypassword" | ConvertTo-SecureString -asPlainText -Force .\Export-AuditReport.ps1 -BaseUrl "https://hostname.example.com/AuditTrail" -DataSource 'TestRepository(localhost)' -ReportId sKXtXgGe1k6tFxEBNcS_yA -ExportFile C:\daily_export\savedReport.csv -Username Example.com\exportAdmin -Password $password .EXAMPLE $ReportName = 'DailyFormsReport_' + (Get-Date -Format "yyyy-MM-dd") + '.csv' $ExportFile = Join-Path -Path 'C:\AuditTrailExports\' -ChildPath $ReportName # C:\AuditTrailExports\DailyFormsReport_2024-06-21.csv $FormsDataSource = '63ca4d94-3b98-4314-817f-ad75f17e1795' $ReportId = 'y72mbzN710WpZMkK-RmAqg' .\Export-AuditReport.ps1 -DataSource $FormsDataSource -ReportId $ReportId -ExportFile $ExportFile -Raw -CheckResultInMinutes 1 -RecheckResultUntilComplete -ReturnValue 'StateString' #> param( [string] $DataSource, [string] $Repository, #legacy compatibility, use $DataSource instead [Parameter(Mandatory=$true)] [string] $ReportId, [Parameter(Mandatory=$true)] [string] $ExportFile, [switch] $Raw = $false, [string] $BaseUrl = 'http://localhost/AuditTrail', [string] $Username, [SecureString] $Password, [string] $Language = 'en', [string] $TimeZone, [int] $CheckResultInMinutes = -1, [switch] $RecheckResultUntilComplete = $false, [string] $ReturnValue = 'token', [string[]] $AdditionalSources = $null, [string[]] $AdditionalRepositories = $null #legacy compatibility, use $AdditionalSources instead ) if (-not $BaseUrl.EndsWith("/")){ $BaseUrl = "$BaseUrl/" } if (-not [string]::IsNullOrEmpty($Username)) { $credential = New-Object System.Management.Automation.PSCredential($Username, $Password) } <# If a -Repository param value is provided and -DataSource is not, the command is likely from a previous version of the script when the -Repository param was mandatory and -DataSource didn't exist. To maintain compatibility with those commands, set $DataSource = $Repository. Since those two parameters can't be set to Mandatory, throw a terminating error here if both are missing. #> if (([string]::IsNullOrEmpty($DataSource)) -and (-not [string]::IsNullOrEmpty($Repository))) { $DataSource = $Repository } elseif ((-not [string]::IsNullOrEmpty($DataSource)) -and (-not [string]::IsNullOrEmpty($Repository))) { Write-Output 'Warning: Both -DataSource and -Repository parameter values were provided. Only the -DataSource value will be used.' } elseif (([string]::IsNullOrEmpty($DataSource)) -and ([string]::IsNullOrEmpty($Repository))) { throw 'A -DataSource (or -Repository) parameter value is required.' } if ($Raw) { $uri = "$($BaseUrl)api/SavedReport/$ReportId/raw?lang=$Language&destFile=$ExportFile&DataSourceId=$DataSource" } else { $uri = "$($BaseUrl)api/SavedReport/$ReportId/export?lang=$Language&destFile=$ExportFile&DataSourceId=$DataSource" } # $uri = "$uri&timeout=$(24* 60 * 60 * 1000)" change the timeout to 24 hours, default 10 hours. if (-not [string]::IsNullOrEmpty($TimeZone)) { $uri = "$uri&tz=$TimeZone" } <# If an -AdditionalReposities param value is provided and -AdditionalSources is not, the command is likely from a previous version of the script when -AdditionalSources didn't exist. To maintain compatibility with those commands, set $AdditionalSources = $AdditionalRepositories #> if (($null -eq $AdditionalSources) -and (($null -ne $AdditionalRepositories) -and ($AdditionalRepositories.Length -gt 0))) { $AdditionalSources = $AdditionalRepositories } elseif ((($null -ne $AdditionalSources) -and ($AdditionalSources.Length -gt 0)) -and (($null -ne $AdditionalRepositories) -and ($AdditionalRepositories.Length -gt 0))) { Write-Output 'Warning: Both -AdditionalSources and -AdditionalRepositories parameter values were provided. Only the -AdditionalSources value will be used.' } if (($null -ne $AdditionalSources) -and ($AdditionalSources.Length -gt 0)) { foreach ($source in $AdditionalSources) { $uri = "$uri&additionalSources=$source" } } $params = @{ 'Method'='POST'; 'Uri'=$uri; } try { if ($null -eq $credential) { $params.Add('UseDefaultCredentials', $true); } else { $params.Add('Credential', $credential); } # When using http and PowerShell version is 6 or higher ('PowerShell' vs 'Windows PowerShell' (<=5)), add `-AllowUnencryptedAuthentication` if ($BaseUrl.StartsWith('http://') -and ( $PSVersionTable.PSVersion.Major -ge 6)) { $params.Add('AllowUnencryptedAuthentication', $true); } $token = Invoke-RestMethod @params } catch { Write-Error $_ return "Export failed, check if you have appropriate rights to run the audit report and that the ReportId is correct." } if ($Raw) { $statusUri = "$($BaseUrl)api/SavedReport/raw/$token/status" } else { $statusUri = "$($BaseUrl)api/SavedReport/export/$token/status" } function Get-ExportStatus { $statusParams = @{ 'Method'='GET'; 'Uri'="$statusUri"; } if ($null -eq $credential) { $statusParams.Add('UseDefaultCredentials', $true); } else { $statusParams.Add('Credential', $credential); } # When using http and PowerShell version is 6 or higher ('PowerShell' vs 'Windows PowerShell' (<=5)), add `-AllowUnencryptedAuthentication` if ($BaseUrl.StartsWith('http://') -and ( $PSVersionTable.PSVersion.Major -ge 6)) { $statusParams.Add('AllowUnencryptedAuthentication', $true); } $status = Invoke-RestMethod @statusParams # status: # { # "Completion": 80, // the overall percent progress of exporting # "State": 1, // 0: not started, 1: exporting, 2: exported, 5: failed, 6: canceled # "FailMsg": "" // reason why the export failed # } return $status } function Get-ExportStateStringByNum { param ([int]$stateNum) $stateStr = switch ($stateNum) { 0 { 'Not Started' } 1 { 'Exporting' } 2 { 'Exported' } 3 { 'Downloading' } # Not used 4 { 'Finished' } # Not used 5 { 'Failed' } 6 { 'Canceled' } default { 'Unknown' } } return $stateStr } # Prevent querying for status as fast as the loop can execute if RecheckResultUntilComplete is $true # and $CheckResultInMinutes is 0, negative, or $null by resetting it to 1 minute. if ($RecheckResultUntilComplete -and ($CheckResultInMinutes -le 0)) { $CheckResultInMinutes = 1 } [int] $i = 0 Do { if (($CheckResultInMinutes -gt 0) -and ($i -gt 0)) { Start-Sleep -Seconds ($CheckResultInMinutes * 60) } else { Start-Sleep -Second 2 } $status = Get-ExportStatus $stateStr = Get-ExportStateStringByNum($status.state) if ($status.state -eq 5 -or $status.state -eq 6) { #Write-Error $status if ($status.state -eq 5) { Write-Error "Export $stateStr at $($status.completion)% completion with message $($status.failMsg)" } elseif ($status.state -eq 6) { Write-Error "Export $stateStr at $($status.completion)% completion" } } elseif ($status.state -eq 3 -or $status.state -eq 4) { # These ExportState values shouldn't occur. Write-Error "An unexpected export state of '$stateStr' was returned. The script will skip any additional status checks." break } if (!$RecheckResultUntilComplete) { break } # Repeat while not Exported (2), Failed (5), or Canceled (6) $continue = ($status.state -ne 2 -and $status.state -ne 5 -and $status.state -ne 6) $i++ } while ($continue) # Return block # Case-insensitive comparisons if ($ReturnValue -eq 'StateNum') { return $status.state } elseif ($ReturnValue -eq 'StateNumAsString') { return $status.state.ToString() } elseif ($ReturnValue -eq 'StateString') { return $stateStr } elseif ($ReturnValue -eq 'Token') { return $token } else { # default to 'token' for invalid values Write-Host "Return value '$ReturnValue' is not a valid parameter input. Defaulting to 'Token'." return $token }