EventMonitor/EMCommon.psm1

<#PSScriptInfo
.AUTHOR Microsoft
.COMPANYNAME Microsoft Corporation
.COPYRIGHT (c) Microsoft Corporation
#>


<#
.SYNOPSIS
    Get the information if there is any active or idle rdp session. Displays information about user sessions on a Remote Desktop Session Host server. You can use this command to find out if a specific user is logged on to a specific Remote Desktop Session Host server. This command returns the following information:
    USERNAME SESSIONNAME ID STATE IDLE TIME LOGON TIME
    >ranavale rdp-tcp#2 1 Active . 5/6/2022 10:37 AM
        - Name of the user
        - Name of the session on the Remote Desktop Session Host server
        - Session ID
        - State of the session (active or disconnected)
        - Idle time (the number of minutes since the last keystroke or mouse movement at the session)
        - Date and time the user logged on
#>

function Get-ActiveUsersByQUsers {
    param(
        [Parameter(Mandatory = $true)]
        [string] $logAnalyticsConString,
        [Parameter(Mandatory = $false)]
        [string] $sessionId,
        [Parameter(Mandatory = $true)]
        [string] $UserName
    )
    Import-Module -Name "$PSScriptRoot\Telemetry\AITelemetry.psm1"
    Import-Module -Name "$PSScriptRoot\LogoffIndicators.psm1"
    Import-Module -Name "$PSScriptRoot\LogonIndicators.psm1"
    $LogFilePath = "$PSScriptRoot\Telemetry\Logs.txt";
    $hasActiveUserSession = $false
    try {
        (query user) | ForEach-Object {
            $conInfo = ($_ -split (" ")) | Where-Object { ![string]::IsNullOrEmpty($_) }

            if ($null -ne $conInfo[0] -and $null -ne $conInfo[4]) {
                if (($conInfo.Count -eq 8) -and $conInfo[3] -eq "Active" -and $conInfo[4] -eq ".") {
                    $hasActiveUserSession = $true
                }

                try { # Do not fail on calculation.
                    if ($conInfo[0] -notlike "*USERNAME*") {
                        if ($conInfo.Count -eq 8) { $i = 0; $sessionName = $conInfo[1] }
                        else { $i = 1; $sessionName ="-"}

                        $iTimer = $conInfo[4 - $i]
                        if ($iTimer -like "*:*") {
                            $hours = $iTimer.split(":")[0];
                            $mins = [convert]::ToInt32($iTimer.split(":")[1], 10);
                            if ($hours -like ("*+*")) {
                                $days = [convert]::ToInt32($hours.split("+")[0], 10);
                                $hours = [convert]::ToInt32($hours.split("+")[1], 10);
                                $mins += ($hours*60) + $($days *1440)
                            } else {
                                $hours = [convert]::ToInt32($hours, 10);
                                $mins += ($hours*60)
                            }
                        }
                        elseif($iTimer -like "*.*") { $mins = 0 }
                        else { $mins = [convert]::ToInt32($iTimer, 10); }
                        $EvProps = New-Object 'system.collections.generic.dictionary[string, string]'
                        $EvProps.Add("SessionId", "$sessionId")
                        $EvProps.Add("USER-NAME", ($conInfo[0]).Trim(">"))
                        $EvProps.Add("SESSION-NAME", $sessionName)
                        $EvProps.Add("SESSION-ID", $conInfo[2 - $i])
                        $EvProps.Add("STATE", $conInfo[3 - $i])
                        $EvProps.Add("IDLE-FOR-LAST(Min)", ($mins))
                        $EvProps.Add("LOGON-TIME", "$($conInfo[5 - $i]) $($conInfo[6 - $i]) $($conInfo[7 - $i])")
                        TrackEvent -Name "Query Active Session connection" -Properties $EvProps -logAnalyticsConString $logAnalyticsConString
                    }
                } catch {}
            }
        }

        return $hasActiveUserSession
    } catch {
        Add-Content -Path $LogFilePath -Value "$(get-date -UFormat %c) :: Exception Message: `n$_.Exception.Message"
        Add-Content -Path $LogFilePath -Value "$(get-date -UFormat %c) :: ScriptStackTrace: `n$_.ScriptStackTrace"
        $LASTEXITCODE = 1
        $ErrorProps = New-Object 'system.collections.generic.dictionary[string, string]'
        $ErrorProps.Add("SessionId", "$sessionId")
        $ErrorProps.Add("UserName", "$UserName")
        $ErrorProps.Add("Function", "Get-ActiveUsersByQUsers")
        TrackException -ErrorRecord $_ -Properties $ErrorProps -logAnalyticsConString $logAnalyticsConString;
    }
}

<#
.SYNOPSIS
    Get the information if current machine has any active ssh connection.
#>

function Get-ActiveSSHDConnectionByNetStat {
    param(
        [Parameter(Mandatory = $true)]
        [string] $logAnalyticsConString,
        [Parameter(Mandatory = $false)]
        [string] $sessionId
    )
    Import-Module -Name "$PSScriptRoot\Telemetry\AITelemetry.psm1"
    $LogFilePath = "$PSScriptRoot\Telemetry\Logs.txt";
    $hasSSHDConnection = $false
    try {
        $ns = netstat -b
            $ns | ForEach-Object {
                if ($_ -like "*sshd.exe*") {
                    $hasSSHDConnection = $true
                }
            }
        if ($hasSSHDConnection) {
            $EvProps = New-Object 'system.collections.generic.dictionary[string, string]'
            $EvProps.Add("SessionId", "$sessionId")
            $EvProps.Add("ACTIVE SSH connection exist", "$true")
            TrackEvent -Name "Query Active SSH connection" -Properties $EvProps -logAnalyticsConString $logAnalyticsConString
        }
        return $hasSSHDConnection
    } catch {
        Add-Content -Path $LogFilePath -Value "$(get-date -UFormat %c) :: Exception Message: `n$_.Exception.Message"
        Add-Content -Path $LogFilePath -Value "$(get-date -UFormat %c) :: ScriptStackTrace: `n$_.ScriptStackTrace"
        $LASTEXITCODE = 1
        $ErrorProps = New-Object 'system.collections.generic.dictionary[string, string]'
        $ErrorProps.Add("SessionId", "$sessionId")
        $ErrorProps.Add("Function", "Get-ActiveSSHDConnectionByNetStat")
        TrackException -ErrorRecord $_ -Properties $ErrorProps -logAnalyticsConString $logAnalyticsConString;
    }
}

<#
.SYNOPSIS
    Get the list of users profile on the current machine.
#>

function Get-WindowsUsers {
    param(
        [Parameter(Mandatory = $true)]
        [string] $logAnalyticsConString,
        [Parameter(Mandatory = $true)]
        [string] $sessionId
    )
    try {
        Import-Module -Name "$PSScriptRoot\Telemetry\AITelemetry.psm1"
        $LogFilePath = "$PSScriptRoot\Telemetry\Logs.txt";
        $users = @()
        $userProfiles = Get-CimInstance -ClassName Win32_UserProfile -Filter "Special = 'False'" | Select-Object *
        $userProfiles | ForEach-Object {
            try {
                $SID = ($_).SID
                $objSID = New-Object System.Security.Principal.SecurityIdentifier($SID)
                $objUser = $objSID.Translate([System.Security.Principal.NTAccount])
                $u = @{
                    UserName = $objUser.Value;
                    SID = ($_).SID;
                    LastUseTime = ($_).LastUseTime;
                    Loaded = ($_).Loaded;
                    Special = ($_).Special;
                    LocalPath = ($_).LocalPath;
                }
                $users += $u
            } catch {
                # continue for next user
            }
        }
        return $users
    } catch {
        Add-Content -Path $LogFilePath -Value "$(get-date -UFormat %c) :: Exception Message: `n$_.Exception.Message"
        Add-Content -Path $LogFilePath -Value "$(get-date -UFormat %c) :: ScriptStackTrace: `n$_.ScriptStackTrace"
        $LASTEXITCODE = 1
        $ErrorProps = New-Object 'system.collections.generic.dictionary[string, string]'
        $ErrorProps.Add("sessionId", "$sessionId")
        $ErrorProps.Add("Function", "Get-WindowsUsers")
        TrackException -ErrorRecord $_ -Properties $ErrorProps -logAnalyticsConString $logAnalyticsConString;
        throw  "Failed to get windows users using CimInstance: $_"
    }
}

function Send-LogAnalyticsConnectEvents {
    param(
        [Parameter(Mandatory = $true)]
        [string] $logAnalyticsConString,
        [Parameter(Mandatory = $true)]
        [string] $eventName,
        [parameter(Mandatory=$true)]
        [system.collections.generic.dictionary[[string], [string]]] $Properties,
        [parameter(Mandatory=$false)]
        [System.Diagnostics.Eventing.Reader.EventRecord]$sendEvent
    )
    Import-Module -Name "$PSScriptRoot\Telemetry\AITelemetry.psm1"
    $LogFilePath = "$PSScriptRoot\Telemetry\Logs.txt";
    $ErrorActionPreference = "Stop"

    try {
        $EvPropsAll = New-Object 'system.collections.generic.dictionary[string, string]'
        foreach($key in $Properties.Keys) { 
            $EvPropsAll.Add($key, $Properties[$key])
        }
        $EvPropsAll.Add("EventId", "$($sendEvent.Id)")
        $EvPropsAll.Add("Message", "$($sendEvent.Message)")
        $EvPropsAll.Add("TimeCreated", "$($sendEvent.TimeCreated)")
        $EvPropsAll.Add("Level", "$($sendEvent.Level)")
        $EvPropsAll.Add("Keywords", "$($sendEvent.Keywords)")
        $EvPropsAll.Add("RecordId", "$($sendEvent.RecordId)")
        $EvPropsAll.Add("ProviderId", "$($sendEvent.ProviderId)")
        $EvPropsAll.Add("ProviderName", "$($sendEvent.ProviderName)")
        $EvPropsAll.Add("ThreadId", "$($sendEvent.ThreadId)")
        $EvPropsAll.Add("MachineName", "$($sendEvent.MachineName)")
        $EvPropsAll.Add("UserId", "$($sendEvent.UserId)")
        $EvPropsAll.Add("ActivityId", "$($sendEvent.ActivityId)")
        $EvPropsAll.Add("RelatedActivityId", "$($sendEvent.RelatedActivityId)")
        $EvPropsAll.Add("ContainerLog", "$($sendEvent.ContainerLog)")
        $EvPropsAll.Add("MatchedQueryIds", "$($sendEvent.MatchedQueryIds)")
        $EvPropsAll.Add("LevelDisplayName", "$($sendEvent.LevelDisplayName)")
        $EvPropsAll.Add("TaskDisplayName", "$($sendEvent.TaskDisplayName)")
        $i = 0
        foreach($p in $sendEvent.Properties) { 
            $i = $i + 1
            $EvPropsAll.Add("Property[$i]", "$($p.Value)")
        }
        TrackEvent -Name $eventName -Properties $EvPropsAll -logAnalyticsConString $logAnalyticsConString
    } catch {
        Add-Content -Path $LogFilePath -Value "$(get-date -UFormat %c) :: Exception Message: `n$_.Exception.Message"
        Add-Content -Path $LogFilePath -Value "$(get-date -UFormat %c) :: ScriptStackTrace: `n$_.ScriptStackTrace"
        $LASTEXITCODE = 1
        TrackException -ErrorRecord $_ -Properties $ErrorProps -logAnalyticsConString $logAnalyticsConString;
    }
}

<#
.SYNOPSIS
    Get user logon/logoff event details, send details to App-Insights and predict the user interaction state with the machine.
.DESCRIPTION
    This function is responsible for reading user interactions with the machine. Based on this events from past timeRangeForEventsBefore number of minutes,
    predict the user state for the machine such as whether user is/was ACTIVE, IDLE or ACTIVE RECENTLY. While the events are read to predict the state, the
    details of the events are also redirected to the azure app-insights to further analyze the Information.
.PARAMETER sessionId
    Specifies a session Id that can reference to session id.
.PARAMETER timeRangeForEventsBefore
    Specifies a interval in minutes to read windows events in previous `x` minutes.
.PARAMETER user
    Specifies a user to read the windows events for.
.EXAMPLE
    Get-WindowsEventsAndSessions 'xyz' '30' 'abc'
#>

function Get-WindowsEventsAndSessions {
    param(
        [Parameter(Mandatory = $true)]
        [string] $logAnalyticsConString,
        [Parameter(Mandatory = $false)]
        [string] $sessionId,
        [Parameter(Mandatory = $true)]
        [DateTime] $timeRangeForEventsBefore,
        [Parameter(Mandatory = $true)]
        [string] $user
    )

    $ErrorActionPreference = "Stop"
    Import-Module -Name "$PSScriptRoot\Telemetry\AITelemetry.psm1"
    Import-Module -Name "$PSScriptRoot\LogoffIndicators.psm1"
    Import-Module -Name "$PSScriptRoot\LogonIndicators.psm1"
    $LogFilePath = "$PSScriptRoot\Telemetry\Logs.txt";

    try {
        Get-UserInteractionEvents -sessionId $sessionId -TimeBefore $timeRangeForEventsBefore -User $user -logAnalyticsConString $logAnalyticsConString;

        Get-UserIdleEvents -sessionId $sessionId -TimeBefore $timeRangeForEventsBefore -User $user -logAnalyticsConString $logAnalyticsConString;

        $hasActiveSession = Get-ActiveUsersByQUsers -sessionId $sessionId -UserName $user -logAnalyticsConString $logAnalyticsConString

        Add-Content -Path $LogFilePath -Value "$(get-date -UFormat %c) :: Machine has active session: $hasActiveSession"

    }
    catch {
        Add-Content -Path $LogFilePath -Value "$(get-date -UFormat %c) :: Exception Message: `n$_.Exception.Message"
        Add-Content -Path $LogFilePath -Value "$(get-date -UFormat %c) :: ScriptStackTrace: `n$_.ScriptStackTrace"
        $ErrorProps = New-Object 'system.collections.generic.dictionary[string, string]'
        $ErrorProps.Add("SessionId", "$sessionId")
        $ErrorProps.Add("Function", "Get-WindowsEventsAndSessions")
        $ErrorProps.Add("user", "$user")
        TrackException -ErrorRecord $_ -Properties $ErrorProps -logAnalyticsConString $logAnalyticsConString;
    }
    finally {
    }
}

<#
.SYNOPSIS
    Create, register and start scheduled task for VM Monitor.
.DESCRIPTION
    The scheduled task will be triggered on any user logon to vm as well as every given $repetitionIntervalInMin intervals
    that watches the windows events triggered by logon and logoff events as well as netstat and quser query results
    to infer if there are any active rdp or ssh session connected to vm.
 
    # Windows VM event monitor
    # This tool contains the scripts that can run as scheduled task under System account and are capable or reading the windows event log and put it to Log Analytics.
    # Logon indication events:
    Gets the details of the latest windows logoff related events and returns the latest event among those events. The list of events considered to be logoff related are
    - `OpenSSHApplication`: OpenSSH/Operational This OpenSSH application event is generated when SSH connected
    - `4648`: This event is generated when a process attempts an account logon by explicitly specifying that account’s credentials..
    - `4624`: This event generates when a logon session is created (on destination machine). It generates on the computer that was accessed, where the session was created.
    - `5140`: This event generates every time network share object was accessed.
    - `4801`: This event is generated when workstation was unlocked.
    - `4634`: This is not a logoff event but session end event, disabled as it gets fired for both logon/logoff
 
    # Logoff indication events:
    Gets the details of the latest windows logoff related events and returns the latest event among those events. The list of events considered to be logoff related are
    - `OpenSSHApplication`: OpenSSH/Operational This OpenSSH application event is generated when SSH disconnect is requested
    - `4647`: This event is generated when a logoff is initiated. No further user-initiated activity can occur for related logon ref.
    - `4779`: This event is generated when a user disconnects from an existing Terminal Services session, or when a user switches away from an existing desktop using Fast User Switching.
    - `4689`: This event is generated when process is terminated. (Disabled for calculations as netstat provides if any ssh connection is active.
 
    # Active Sessions and SSH connection:
    `quser` Event Monitor gathers the data about which users and active sessions exist on the machine. It will send the log with each session's idle time. `netstat -b` gives the indication if there is any active ssh connection to the machine.
 
    # Miscellaneous windows events:
    In addition to logon and logoff related events this tools is `capable of tracking miscellaneous windows event` that can be dynamically provided as input to task while its registration.
 
    `Version: Scripts to VM Event Monitor`
 
.PARAMETER scheduledTaskName
    Specifies a scheduled task name to register with.
.PARAMETER taskScriptPath
    Specifies a script path to be executed by scheduled task.
.PARAMETER repetitionIntervalInMin
    Specifies a interval in minutes to trigger scheduled task repeatedly.
.PARAMETER watchEventsTimeWindowInMin
    Specifies a interval in minutes to read windows events in previous `x` minutes.
.PARAMETER sessionId
    Specifies a session Id that can reference to session id.
.EXAMPLE
    Register-VMMonitorTask `
        -logAnalyticsConString "<your azure logAnalytics connection string>" `
        -sessionId <Sessionid or Guid to identify uniquely in log analytics> `
        -scheduledTaskName "WinEventMonitor"
        -repetitionIntervalInMin 5
        -watchEventsTimeWindowInMin 5
        -trackMiscellaneousEvents "Security-::System-41,1074,1076,6005,6006,6008,6009,6013::Application-"
#>


Function Register-VMMonitorTask {
    param(
        [Parameter(Mandatory = $true)]
        [string] $logAnalyticsConString,
        [Parameter(Mandatory = $false)]
        [string] $sessionId = [guid]::NewGuid().Guid,
        [Parameter(Mandatory = $false)]
        [Int32] $watchEventsTimeWindowInMin = 5,
        [Parameter(Mandatory = $false)]
        [Int32] $repetitionIntervalInMin = 5,
        [Parameter(Mandatory = $false)]
        [string] $scheduledTaskName = "WinEventMonitor",
        [Parameter(Mandatory = $false)]
        [string] $trackMiscellaneousEvents = "Security-::System-41,1074,1076,6005,6006,6008,6009,6013::Application-"
    )

    $LogFilePath = "$PSScriptRoot\Telemetry\Logs.txt";
    Write-Host "Logs can be found at: $LogFilePath"    
    $taskScriptPath = "$PSScriptRoot\Start-EventMonitor.ps1";
    Set-Content -Path "$PSScriptRoot\Telemetry\LogAnalyticsConString.txt" -Value $logAnalyticsConString -Force

    Add-Content -Path $LogFilePath -Value "`n`n"
    Add-Content -Path $LogFilePath -Value "========================================================================"    
    Add-Content -Path $LogFilePath -Value "$(get-date -UFormat %c) :: Starting NEW Scheduled Task Id: $sessionId"
    Add-Content -Path $LogFilePath -Value "========================================================================"
    Add-Content -Path $LogFilePath -Value "$(get-date -UFormat %c) :: Starting a Scheduled task executing $taskScriptPath"
    Add-Content -Path $LogFilePath -Value "$(get-date -UFormat %c) :: Logs will be sent to LogAnalytics connection string - $logAnalyticsConString"
    Add-Content -Path $LogFilePath -Value "$(get-date -UFormat %c) :: Current scheduled task LogAnalytics connection string at: $PSScriptRoot\Telemetry\LogAnalyticsConString.txt"
    #Add-Content -Path $LogFilePath -Value "$(get-date -UFormat %c) :: Starting $taskScriptPath Task with $taskScriptPath -sessionId $sessionId -watchEventsTimeWindowInMin $watchEventsTimeWindowInMin -trackMiscellaneousEvents $trackMiscellaneousEvents -logAnalyticsConString $logAnalyticsConString"

    try {
        $emTaskAction = New-ScheduledTaskAction `
            -Execute "pwsh.exe" `
            -Argument "$taskScriptPath -sessionId $sessionId -watchEventsTimeWindowInMin $watchEventsTimeWindowInMin -trackMiscellaneousEvents $trackMiscellaneousEvents -logAnalyticsConString $logAnalyticsConString"
        $emSTPrincipal = New-ScheduledTaskPrincipal `
            -GroupId "NT AUTHORITY\SYSTEM" `
            -RunLevel Highest
        $emTaskSettings = New-ScheduledTaskSettingsSet `
            -Priority 4
        
        # Trigger on Log-off events
        $CIMTriggerClass = Get-CimClass -ClassName MSFT_TaskEventTrigger -Namespace Root/Microsoft/Windows/TaskScheduler:MSFT_TaskEventTrigger
        $Trigger4647 = New-CimInstance -CimClass $CIMTriggerClass -ClientOnly
        $Trigger4647.Subscription = 
        @"
<QueryList><Query Id="0" Path="Security"><Select Path="Security">*[System[Provider[@Name='Microsoft-Windows-Security-Auditing'] and EventID=4647]]</Select></Query></QueryList>
"@

        $Trigger4647.Enabled = $True 
        # Trigger on Other Log-on/Log-off events
        $CIMTriggerClass = Get-CimClass -ClassName MSFT_TaskEventTrigger -Namespace Root/Microsoft/Windows/TaskScheduler:MSFT_TaskEventTrigger
        $Trigger4779 = New-CimInstance -CimClass $CIMTriggerClass -ClientOnly
        $Trigger4779.Subscription = 
        @"
<QueryList><Query Id="0" Path="Security"><Select Path="Security">*[System[Provider[@Name='Microsoft-Windows-Security-Auditing'] and EventID=4779]]</Select></Query></QueryList>
"@

        $Trigger4779.Enabled = $True 
        # Timer Trigger
        $emTimerTasktrigger = New-ScheduledTaskTrigger `
            -RepetitionInterval (New-TimeSpan -Minutes $repetitionIntervalInMin) `
            -Once `
            -At (get-date)
        # Log-on trigger
        $emLogonTasktrigger = New-ScheduledTaskTrigger -AtLogOn
        $evTask = Register-ScheduledTask $scheduledTaskName `
            -Action $emTaskAction `
            -Principal $emSTPrincipal `
            -Settings $emTaskSettings `
            -Trigger $emTimerTasktrigger, $emLogonTasktrigger, $Trigger4647, $Trigger4779
        $evTask | Start-ScheduledTask
    }
    catch {
        Add-Content -Path $LogFilePath -Value "$(get-date -UFormat %c) :: Exception Message: `n$_.Exception.Message"
        Add-Content -Path $LogFilePath -Value "$(get-date -UFormat %c) :: ScriptStackTrace: `n$_.ScriptStackTrace"
        throw "Failed to start VM event monitor task: $_"
    }
}

function Unregister-EventMonitor {
    param(
        [Parameter(Mandatory = $true)]
        [string] $TaskName
    )

    $LogFilePath = "$PSScriptRoot\Telemetry\Logs.txt";

    try {
        Unregister-ScheduledTask -TaskName $TaskName
        Add-Content -Path $LogFilePath -Value "$(get-date -UFormat %c) :: Unregister event monitor task $TaskName"

    }
    catch {
        Add-Content -Path $LogFilePath -Value "$(get-date -UFormat %c) :: Exception Message: `n$_.Exception.Message"
        Add-Content -Path $LogFilePath -Value "$(get-date -UFormat %c) :: ScriptStackTrace: `n$_.ScriptStackTrace"
        throw "Failed to unregister VM event monitor task: $_"
    }
}

function Start-EventMonitor {
    param(
        [Parameter(Mandatory = $true)]
        [string] $TaskName
    )

    $LogFilePath = "$PSScriptRoot\Telemetry\Logs.txt";

    try {
        Start-ScheduledTask -TaskName $TaskName
        Add-Content -Path $LogFilePath -Value "$(get-date -UFormat %c) :: Start event monitor task $TaskName"

    }
    catch {
        Add-Content -Path $LogFilePath -Value "$(get-date -UFormat %c) :: Exception Message: `n$_.Exception.Message"
        Add-Content -Path $LogFilePath -Value "$(get-date -UFormat %c) :: ScriptStackTrace: `n$_.ScriptStackTrace"
        throw "Failed to start VM event monitor task: $_"
    }
}

function Stop-EventMonitor {
    param(
        [Parameter(Mandatory = $true)]
        [string] $TaskName
    )

    $LogFilePath = "$PSScriptRoot\Telemetry\Logs.txt";

    try {
        Stop-ScheduledTask -TaskName $TaskName
        Add-Content -Path $LogFilePath -Value "$(get-date -UFormat %c) :: Stop event monitor task $TaskName"

    }
    catch {
        Add-Content -Path $LogFilePath -Value "$(get-date -UFormat %c) :: Exception Message: `n$_.Exception.Message"
        Add-Content -Path $LogFilePath -Value "$(get-date -UFormat %c) :: ScriptStackTrace: `n$_.ScriptStackTrace"
        throw "Failed to stop VM event monitor task: $_"
    }
}

function Get-EventMonitor {
    param(
        [Parameter(Mandatory = $true)]
        [string] $TaskName
    )

    $LogFilePath = "$PSScriptRoot\Telemetry\Logs.txt";

    try {
        Get-ScheduledTask -TaskName $TaskName
        Add-Content -Path $LogFilePath -Value "$(get-date -UFormat %c) :: Get event monitor task $TaskName"

    }
    catch {
        Add-Content -Path $LogFilePath -Value "$(get-date -UFormat %c) :: Exception Message: `n$_.Exception.Message"
        Add-Content -Path $LogFilePath -Value "$(get-date -UFormat %c) :: ScriptStackTrace: `n$_.ScriptStackTrace"
        throw "Failed to stop VM event monitor task: $_"
    }
}

function Disable-EventMonitor {
    param(
        [Parameter(Mandatory = $true)]
        [string] $TaskName
    )

    $LogFilePath = "$PSScriptRoot\Telemetry\Logs.txt";

    try {
        Disable-EventMonitor -TaskName $TaskName
        Add-Content -Path $LogFilePath -Value "$(get-date -UFormat %c) :: Disable event monitor task $TaskName"

    }
    catch {
        Add-Content -Path $LogFilePath -Value "$(get-date -UFormat %c) :: Exception Message: `n$_.Exception.Message"
        Add-Content -Path $LogFilePath -Value "$(get-date -UFormat %c) :: ScriptStackTrace: `n$_.ScriptStackTrace"
        throw "Failed to disable VM event monitor task: $_"
    }
}

function Enable-EventMonitor {
    param(
        [Parameter(Mandatory = $true)]
        [string] $TaskName
    )

    $LogFilePath = "$PSScriptRoot\Telemetry\Logs.txt";

    try {
        Enable-EventMonitor -TaskName $TaskName
        Add-Content -Path $LogFilePath -Value "$(get-date -UFormat %c) :: Enable event monitor task $TaskName"

    }
    catch {
        Add-Content -Path $LogFilePath -Value "$(get-date -UFormat %c) :: Exception Message: `n$_.Exception.Message"
        Add-Content -Path $LogFilePath -Value "$(get-date -UFormat %c) :: ScriptStackTrace: `n$_.ScriptStackTrace"
        throw "Failed to ennable VM event monitor task: $_"
    }
}