You are viewing limited content. For full access, please sign in.

Question

Question

schedule audit trail reports

asked on November 20, 2023

Is there an option to generate monthly and daily reports on Laserfiche Audit trail using scheduled jobs with-out manually editing the date range in UI. The UI give option "Relative Date Range" but it doesn't work for daily reports unless the schedule runs exactly at midnight and I choose the range as one day.

I am looking for options to generate last days report using a scheduled job. 

0 0

Replies

replied on November 21, 2023 • Show version history

Hi, Aju,

 

Audit Trail has not formally supported scheduled report. As a workaround, I wrote a script Update-ReportToLastDay.ps1 that modifies the date range of an existing saved report to the last day (in time zone of the machine where the script runs). Combined with the script Export-AuditReport.ps1 (mentioned in Audit Trail 11 FAQ), it is viable to work out a scheduled task that can meet your need. The basic workflow is likely to be:

 

  1. On the Audit Trail Reporting page, design a report by applying filters as desired. You can leave the "Date range" filter as it is. The filter will be replaced in Update-ReportToLastDay.ps1
  2. Use the Configure Column Display dialog box to select the columns to include in the report.
  3. Save the report and note the report ID visible from the browser address bar:

    https://ServerName/AuditTrail/reports/ABCD1234EFGH-IJKLM?lang=en&dataSource=repositoryandserver&view=table

  4. Open the Task Scheduler on Windows to create a scheduled task
  5. Use this task to run the PowerShell script with the following parameters
    Update-ReportToLastDay.ps1 -Repository "repositoryname(servername)" -ReportId "ABCD1234EFGH-IJKLM"
    Export-AuditReport.ps1 -Repository "repositoryname(servername)" -ReportId "ABCD1234EFGH-IJKLM" -ExportFile "reportfilename.xlsx"
    
  6. By default, the exported report file will be located in C:\ProgramData\Laserfiche\AuditAnalytics\Export.

 

I've tried the script Update-ReportToLastDay.ps1 on latest version (11.0.2306.3549) of Audit Trail. Note that the script relies on some Audit Trail implementation details that may not be guaranteed in future Audit Trail release.

 

Update-ReportToLastDay.ps1:

# Copyright (c) Laserfiche.

param(
  [Parameter(Mandatory=$true)]
  [string] $Repository,
  [Parameter(Mandatory=$true)]
  [string] $ReportId,
  [string] $Username,
  [SecureString] $Password,
  [string] $BaseUrl = "http://localhost/AuditTrail"
)

if(-not $BaseUrl.EndsWith("/"))
{
    $BaseUrl = "$BaseUrl/"
}

if (-not [string]::IsNullOrEmpty($Username))
{
    $credential = New-Object System.Management.Automation.PSCredential($Username, $Password)
}

if ($credential -eq $null)
{
    $savedReportResponse = Invoke-WebRequest "$($BaseUrl)api/SavedReport/Get?reportId=$($ReportId)&dataSource=$($Repository)&isDraft=false" -UseDefaultCredentials
}
else
{
    $savedReportResponse = Invoke-WebRequest "$($BaseUrl)api/SavedReport/Get?reportId=$($ReportId)&dataSource=$($Repository)&isDraft=false" -Credential $credential    
}

$startDate = (Get-Date).AddDays(-1).ToString("yyyy-MM-dd")
$endDate = (Get-Date).ToString("yyyy-MM-dd")
$timeFilter = @{
      "colDisplayName"="Event time";
      "colId"="eventTime";
      "predDisplayName"="by date";
      "predId"="IN_DATE_RANGE";
      "nodeID"=0;
      "fieldType"= "DATETIME";
      "dateRange"= @{
        "rangeType"= "RANGE";
        "startTimeObj"= @{
          "datetime"= (Get-Date -Date $startDate).ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ss.000Z");
          "isDateValid"= $true;
          "isTimeValid"= $false;
          "utcDateString"= (Get-Date -Date $startDate).ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ss.000Z")
        };
        "endTimeObj"= @{
          "datetime"= (Get-Date -Date $endDate).ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ss.000Z");
          "isDateValid"= $true;
          "isTimeValid"= $false;
          "utcDateString"= (Get-Date -Date $endDate).ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ss.000Z")
        };
        "ageInDays"=7
      };
      "escape"= $false
    }
    
$savedReport = $savedReportResponse.Content | ConvertFrom-Json
$otherPreFilters = $savedReport.preFilters | Where {$_.colId -ne "eventTime"}
if ($otherPreFilters -ne $null)
{
    $savedReport.preFilters = $otherPreFilters,$timeFilter
}
else
{
    $savedReport.preFilters[0] = $timeFilter
}

if ($credential -eq $null)
{
    Invoke-WebRequest -Method POST "$($BaseUrl)api/SavedReport/Post?reportName=$($savedReport.reportName)&reportId=$($ReportId)&shared=true&force=true&dataSource=$($Repository)" -body $($savedReport | ConvertTo-Json -Depth 20) -UseDefaultCredentials -ContentType "application/json" 
}
else
{
    Invoke-WebRequest -Method POST "$($BaseUrl)api/SavedReport/Post?reportName=$($savedReport.reportName)&reportId=$($ReportId)&shared=true&force=true&dataSource=$($Repository)" -body $($savedReport | ConvertTo-Json -Depth 20) -Credential $credential -ContentType "application/json" 
}

 

2 0
replied on April 15

Hi Jiajun Hu,

Thank you for sharing your script. I’m wondering if you have an updated version of Update-ReportToLastDay.ps1 that works with Audit Trail version 11.0.2412.49. (Laserfiche 12). I tried making some modifications at the script, but I may be doing something wrong. The error I’m receiving is:

Invoke-WebRequest : {"message":"Bad request: need to specify the repository.","isError":true,"errorCode":0}

If you have a newer version of the script or any guidance on how to adapt it for this Audit Trail release, I would really appreciate it.

Thank you,
Mario

 

0 0
replied on April 16

Hi, Mario,

 

I've tried the original Update-ReportToLastDay.ps1 on latest Audit Trail and it still works. Could you try the original version of Update-ReportToLastDay.ps1?

 

Regards,

0 0
replied on April 17

Hi Jiajun,

Thank you for doing the exercise. I tried the original version again, but the error is still the same. I modified the script slightly to include the content of the $saveReport variable in case it helps confirm that the first Invoke-WebRequest call is working. I also added the -Verbose parameter to the second Invoke-WebRequest call. I also ran the script with a newer version of PS (7.5.4).

As a reference, I am able to run the Export-AuditReport.ps1 script regularly without issues.

I really appreciate any tips or help that might shed some light on this.

Thank you.
 

0 0
replied on April 20 • Show version history

Could you check the two repository names you redacted in the screenshot are exactly same string? Direct cause of the error is that Audit Trail see different repository names, though I don't know how that can happen. Irrelevant to the error though, I see the report name you use is "Deletions" which is a reserved report that cannot be updated. You may create a new report to use the script.

 

If the issue persists, I think it will be hard to troubleshoot it here. A support case may be appropriate.

0 0
replied on April 20 • Show version history

Hi Jiajun,

I appreciate your support. The issue was definitely the repository name. The difference was the uppercase letters I used for the server name in the ServerName parameter. I assumed it wasn’t case‑sensitive like the Export-AuditReport.ps1 script, but when I checked the URL string used in the Audit Trail tool, the datasource parameter was in lowercase. I copied it exactly as shown, and the report worked.

Thank you for your support!

In case someone needs it, and hoping I’m not overstepping, I added two modifications to your script, which I’m copying below, so we can set the desired date ranges as well as specify a user to audit. I hope this helps others — it worked for me. Any modifications I made are marked with the comment “Mario Grau”.

To call the additional parameters:

.\Update-ReportStartEndDays.ps1 -Repository repositoryname(servername)" -ReportId "ABCD1234EFGH-IJKLM" -LFStartDate 2026-04-18 -LFEndDate 2026-04-19 -userFilter "Domain\Username"

The modified script:

# Copyright (c) Laserfiche.

param(
  [Parameter(Mandatory=$true)]
  [string] $Repository,
  [Parameter(Mandatory=$true)]
  [string] $ReportId,
  [Parameter(Mandatory=$false)]
  # N E W   P A R A M E T E R S (Mario Grau)
  [datetime] $LFStartDate,
  [Parameter(Mandatory=$false)]
  [datetime] $LFEndDate,
  [Parameter(Mandatory=$false)]
  [string] $userFilter = "Everyone",
  # End - N E W   P A R A M E T E R S (Mario Grau)
  [string] $Username,
  [SecureString] $Password,
  [string] $BaseUrl = "http://localhost/AuditTrail"
)

if(-not $BaseUrl.EndsWith("/"))
{
    $BaseUrl = "$BaseUrl/"
}

if (-not [string]::IsNullOrEmpty($Username))
{
    $credential = New-Object System.Management.Automation.PSCredential($Username, $Password)
}

if ($credential -eq $null)
{
    $savedReportResponse = Invoke-WebRequest "$($BaseUrl)api/SavedReport/Get?reportId=$($ReportId)&dataSource=$($Repository)&isDraft=false" -UseDefaultCredentials #-AllowUnencryptedAuthentication    #$savedReportResponse
}
else
{
    $savedReportResponse = Invoke-WebRequest "$($BaseUrl)api/SavedReport/Get?reportId=$($ReportId)&dataSource=$($Repository)&isDraft=false" -Credential $credential
}

# ************************************************************
# N E W   P A R A M E T E R S (Mario Grau)
# ************************************************************
# If not Start Date is defined, use the day before the current date 
if ($LFStartDate -eq $null)
{   $startDate = (Get-Date).AddDays(-1).ToString("yyyy-MM-dd")
}
else
{
    $startDate = $LFStartDate.ToString("yyyy-MM-dd")
}

# If not End Date is defined, use the current date 
if ($LFStartDate -eq $null)
{   $endDate = (Get-Date).ToString("yyyy-MM-dd")
}
else
{
    $endDate = $LFEndDate.ToString("yyyy-MM-dd")
}
# ************************************************************

$timeFilter = @{
      "colDisplayName"="Event time";
      "colId"="eventTime";
      "predDisplayName"="by date";
      "predId"="IN_DATE_RANGE";
      "nodeID"=0;
      "fieldType"= "DATETIME";
      "dateRange"= @{
        "rangeType"= "RANGE";
        "startTimeObj"= @{
          "datetime"= (Get-Date -Date $startDate).ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ss.000Z");
          "isDateValid"= $true;
          "isTimeValid"= $false;
          "utcDateString"= (Get-Date -Date $startDate).ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ss.000Z")
        };
        "endTimeObj"= @{
          "datetime"= (Get-Date -Date $endDate).ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ss.000Z");
          "isDateValid"= $true;
          "isTimeValid"= $false;
          "utcDateString"= (Get-Date -Date $endDate).ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ss.000Z")
        };
        "ageInDays"=7
      };
      "escape"= $false
    }

$savedReport = $savedReportResponse.Content | ConvertFrom-Json

# Remove any existing User filter (Mario Grau)
$otherPreFilters = $savedReport.preFilters | Where {$_.colId -ne "eventTime" -and $_.colId -ne "user.userName"}
if ($otherPreFilters -ne $null)
{
    $savedReport.preFilters = $otherPreFilters,$timeFilter
}
else
{
    $savedReport.preFilters[0] = $timeFilter
}

# ************************************************************
# N E W   P A R A M E T E R S  -  U S E R (Mario Grau)
# ************************************************************
# If User audit is defined
if ($userFilter -ne "Everyone")
{   Write-Output "userFilter:" $userFilter
    $userParameter = @{
        "colDisplayName"="Name";
        "colId"="user.userName";
        "predDisplayName"="is equal to";
        "predId"="EQUAL_TO_STRING";
        "nodeID"=0;
        "fieldType"= "STRING";
        "value"=$userFilter;
        "escape"= $false
    }

    $otherPreFilters = $savedReport.preFilters | Where {$_.colId -ne "user.userName"}
    if ($otherPreFilters -ne $null)
    {
        $savedReport.preFilters = $otherPreFilters,$userParameter
    }
    else
    {
        $savedReport.preFilters[0] = $userParameter
    }
    # To see the JSON content 
    # $savedReport | Out-File "C:\Users\$env:USERNAME\Documents\Audit Reports\ReportResponseJSON.txt"
}
# ************************************************************


if ($credential -eq $null)
{
    Invoke-WebRequest -Method POST "$($BaseUrl)api/SavedReport/Post?reportName=$($savedReport.reportName)&reportId=$($ReportId)&shared=true&force=true&dataSource=$($Repository)" -body $($savedReport | ConvertTo-Json -Depth 20) -UseDefaultCredentials -ContentType "application/json" 

}
else
{
    Invoke-WebRequest -Method POST "$($BaseUrl)api/SavedReport/Post?reportName=$($savedReport.reportName)&reportId=$($ReportId)&shared=true&force=true&dataSource=$($Repository)" -body $($savedReport | ConvertTo-Json -Depth 20) -Credential $credential -ContentType "application/json" 
}

 

1 0
replied on November 22, 2023

Thanks Jiajun Hu, I believe this work-around will help us to automate our daily reports.

Appreciate your support!

0 0
You are not allowed to follow up in this post.

Sign in to reply to this post.