Query about MilestonePSTools Powershell Module to back-up audit logs

Hello,

I’m testing MilestonePSTools Powershell Module in below condition and facing and error message.

Please help me if you have any idea.

[condition](javascript:void(0); “condition”)

1. XProtect Corporate 2018 R1

2. MSSQL 2016 Evaluation(Enterprise)

3. Windows Server 2012 R2 Standard

4. Powershell 5.1

5. .Net Framework 4.8

6. ​All servers are in Domain environment

7. Management Servers are bonded by Microsoft Failover Cluster Service. (Cluster Service name: VMSCluster)

I entered,

Connect-ManagementServer -Server VMSCluster

Get-Log -LogType Audit -Tail -Minutes 1440 | Export-Csv -Path [c:\1\auditLogs.csv](file:c:/1/auditLogs.csv) -NoTypeInformation

and faced error message as below.

I really don’t know what to do next.

Please help me if you have any idea.

Thank you.

I’ll take a look at this tomorrow. Does the System LogType throw the same error?

What version of MilestonePSTools do you have installed?

What does Get-Command Get-Log return?​

I’m assuming Management Client shows the system and audit logs without issue?​

It seems like it should​ be working so I’ll have to look at whether that error comes from .NET or from the Management Server.

I installed MilestonePSTools by below commands.

Install-Module MilestonePSTools -Scope CurrentUser

Install-Module MilestonePSTools

Update-Module MilestonePSTools

I think MilestonePSTools 1.0.89 has been installed.

If you guide me how to check its version, I will update it asap.

I also tried System LogType and it threw same error as below.

I tried Get-Command Get-Log and received below message

PS [C:\Users\administrator.VMS](file:C:/Users/administrator.VMS)> Get-Command Get-Log

CommandType Name Version Source

----------- ---- ------- ------

Cmdlet Get-Log 1.0.89 MilestonePSTools

I’m thinking the issue is that the LogClient in 2020 R3 MIP SDK is unable to communicate with the Log Servers older than 2018 R3.

We implemented a new Log Server and made breaking changes requiring recompile to support the newer Log Server and I’m assuming the reverse is also true - the 2018 R3 and later SDK’s are unable to communicate with the old log server component.

Here are some release notes for 2018 R3 where this is mentioned

Restructuring MilestonePSTools to enable the user to select the SDK version they want to use is probably the only way to make Get-Log work in this situation and that is a fairly large undertaking that I am not prepared to do.

If it’s not feasible to upgrade to at least 2018 R3, I’m afraid the only option may be to write this yourself either in C# or PowerShell using the 2018 R1 MIP SDK binaries. Here’s how the Get-Log cmdlet is written in C# for reference:

using System;
using System.Collections;
using System.Management.Automation;
using VideoOS.Platform.Log;
 
namespace MilestonePSTools.Commands
{
    /// <summary>
    /// <para type="synopsis">Gets log entries from the Log Server component of a Milestone XProtect VMS system.</para>
    /// <para type="description">This cmdlet brings the contents of the System, Audit, and Rules logs to PowerShell. These are the same log entries available through Management Client, and do not include application/component log files typically found under C:\ProgramData\Milestone\..</para>
    /// <example>
    ///     <code>C:\PS>Get-Log</code>
    ///     <para>Gets the log entries for the last 24 hours from the System log.</para>
    ///     <para/><para/><para/>
    /// </example>
    /// <example>
    ///     <code>C:\PS>Get-Log -LogType Audit -Tail -Minutes 10</code>
    ///     <para>Gets the audit log entries for the last 10 minutes.</para>
    ///     <para/><para/><para/>
    /// </example>
    /// <example>
    ///     <code>C:\PS>Get-Log -LogType Audit -BeginTime '2019-06-05 08:00:00' -EndTime '2019-06-05 11:00:00'</code>
    ///     <para>Gets the audit log entries for the time between 8am and 11am local time on the 5th of June.</para>
    ///     <para/><para/><para/>
    /// </example>
    /// <example>
    ///     <code>C:\PS>Get-Log -LogType Rules -BeginTime '2019-06-05 08:00:00Z' -EndTime '2019-06-05 11:00:00Z'</code>
    ///     <para>Gets the audit log entries for the time between 8am and 11am UTC time on the 5th of June. If run from a system with a UTC offset of -7, then the results would be from between 1am and 4am local time.</para>
    ///     <para/><para/><para/>
    /// </example>
    /// </summary>
    [Cmdlet(VerbsCommon.Get, "Log", DefaultParameterSetName = "TimestampFilter")]
    [OutputType(typeof(PSObject))]
    public class GetLog : ConfigApiCmdlet
    {
        /// <summary>
        /// <para type="description">Specifies the type of log entries to retrieve. Default is System. Not to be confused with the System log from Windows Event Logs.</para>
        /// <para type="description">System logs include events like motion started/stopped, connection errors and license errors.</para>
        /// <para type="description">Audit logs include user actions like logon, playback and exporting. Note: Not all user actions are automatically audited and extensive audit logging must be enabled manually from Management Client > Tools > Options > Server Logs.</para>
        /// <para type="description">Rules logs include any log entry generated within the set of actions defined in the Rules section in Management Client.</para>
        /// </summary>
        [Parameter(Position = 1, ParameterSetName = "TimestampFilter")]
        [Parameter(Position = 2, ParameterSetName = "Tail")]
        [ValidateSet(validValues: new[] {"System", "Audit", "Rules"}, IgnoreCase = true)]
        public string LogType { get; set; } = "System";
 
        /// <summary>
        /// <para type="description">Specifies point in time to begin the log entry search. Default is 24 hours before the current time.</para>
        /// </summary>
        [Parameter(Position = 3, ParameterSetName = "TimestampFilter")]
        public DateTime BeginTime { get; set; } = DateTime.UtcNow.AddHours(-24);
 
        /// <summary>
        /// <para type="description">Specifies the point in time to end the log entry search. Default is the current time.</para>
        /// </summary>
        [Parameter(Position = 4, ParameterSetName = "TimestampFilter")]
        public DateTime EndTime { get; set; } = DateTime.UtcNow;
 
        /// <summary>
        /// <para type="description">Get the last Minutes minutes worth of log entries.</para>
        /// </summary>
        [Parameter(Position = 5, ParameterSetName = "Tail")]
        public SwitchParameter Tail { get; set; }
 
        /// <summary>
        /// <para type="description">The number of minutes before now to retrieve the logs. Default is the last 60 minutes.</para>
        /// </summary>
        [Parameter(Position = 6, ParameterSetName = "Tail")]
        public long Minutes { get; set; } = 60;
 
        /// <summary>
        /// 
        /// </summary>
        protected override void BeginProcessing()
        {
            base.BeginProcessing();
            VideoOS.Platform.SDK.Log.Environment.Initialize();
        }
 
        /// <summary>
        /// 
        /// </summary>
        protected override void ProcessRecord()
        {
            BeginTime = BeginTime.ToUniversalTime();
            EndTime = EndTime.ToUniversalTime();
            try
            {
                var beginTime = Tail
                    ? DateTime.UtcNow - TimeSpan.FromMinutes(Minutes)
                    : BeginTime;
                var endTime = Tail
                    ? DateTime.UtcNow
                    : EndTime;
                var page = 1;
                bool readNextPage;
                do
                {
                    LogClient.Instance.ReadLog(Connection.CurrentSite.FQID.ServerId, page++, out var result, out var columns, LogType, beginTime, endTime);
                    foreach (ArrayList entry in result)
                    {
                        var record = new PSObject();
                        for (var i = 1; i < entry.Count; i++)
                        {
                            record.Properties.Add(
                                new PSVariableProperty(
                                    new PSVariable(columns[i - 1] as string, entry[i] as string)));
                        }
                        WriteObject(record);
                    }
                    readNextPage = result.Count == 1000;
                } while (readNextPage);
            }
            catch (Exception ex)
            {
                WriteExceptionError(ex);
            }
        }
    }
}