Public/Test-CCMStaleLog.ps1
Function Test-CCMStaleLog { <# .SYNOPSIS Returns a boolean based on whether a log file has been written to in the timeframe specified .DESCRIPTION This function is used to check the LastWriteTime property of a specified file. It will be compared to the *Stale parameters. Note that logs are assumed to be under the MEMCM Log directory. Note that this function uses the CIM_DataFile so that SMB is NOT needed. Get-CimInstance is able to query for file information. .PARAMETER LogFileName Name of the log file under the CCM\Logs directory to check. Not, online the log name is required. The path for the MEMCM logs will be automatically identified. The .log extension is optional .PARAMETER DaysStale Number of days of inactivity that you would consider the specified log stale. .PARAMETER HoursStale Number of days of inactivity that you would consider the specified log stale. .PARAMETER MinutesStale Number of minutes of inactivity that you would consider the specified log stale. .PARAMETER DisableCCMSetupFallback Disable the CCMSetup fallback check - details below. When the desired log file is not found, then the last modified timestamp for the CCMSetup log is checked. When the CCMSetup file has activity within the last 24 hours, then we assume that, even though our desired log file was not found, it isn't stale because the MEMCM client is recently installed or repaired. If the CCMSetup is found, and has no activity, or is just not found, then we assume the desired log is 'stale.' This additional chack can be disabled with this switch parameter. .PARAMETER CimSession CimSessions to check the stale log on. .PARAMETER ComputerName Computer Names to check the stale log on. .PARAMETER PSSession PSSessions to check the stale log on. .PARAMETER ConnectionPreference Determines if the 'Get-CCMConnection' function should check for a PSSession, or a CIMSession first when a ComputerName is passed to the function. This is ultimately going to result in the function running faster. The typical use case is when you are using the pipeline. In the pipeline scenario, the 'ComputerName' parameter is what is passed along the pipeline. The 'Get-CCMConnection' function is used to find the available connections, falling back from the preference specified in this parameter, to the the alternative (eg. you specify, PSSession, it falls back to CIMSession), and then falling back to ComputerName. Keep in mind that the 'ConnectionPreference' also determines what type of connection / command the ComputerName parameter is passed to. .EXAMPLE C:\PS> Test-CCMStaleLog -LogFileName ccmexec -DaysStale 2 Check if the ccmexec log file has been written to within the last 2 days on the local computer .EXAMPLE C:\PS> Test-CCMStaleLog -LogFileName AppDiscovery.log -DaysStale 7 -ComputerName Workstation123 Check if the AppDiscovery.log file has been written to within the last 7 days on Workstation123 .NOTES FileName: Test-CCMStaleLog.ps1 Author: Cody Mathis Contact: @CodyMathis123 Created: 2020-01-25 Updated: 2020-02-27 #> [CmdletBinding(DefaultParameterSetName = 'ComputerName')] param ( [Parameter(Mandatory = $true)] [string]$LogFileName, [Parameter(Mandatory = $false)] [int]$DaysStale, [Parameter(Mandatory = $false)] [int]$HoursStale, [Parameter(Mandatory = $false)] [int]$MinutesStale, [Parameter(Mandatory = $false)] [switch]$DisableCCMSetupFallback, [Parameter(Mandatory = $false, ValueFromPipelineByPropertyName = $true, ParameterSetName = 'CimSession')] [Microsoft.Management.Infrastructure.CimSession[]]$CimSession, [Parameter(Mandatory = $false, ValueFromPipelineByPropertyName = $true, ParameterSetName = 'ComputerName')] [Alias('Connection', 'PSComputerName', 'PSConnectionName', 'IPAddress', 'ServerName', 'HostName', 'DNSHostName')] [string[]]$ComputerName = $env:ComputerName, [Parameter(Mandatory = $false, ParameterSetName = 'PSSession')] [Alias('Session')] [System.Management.Automation.Runspaces.PSSession[]]$PSSession, [Parameter(Mandatory = $false, ParameterSetName = 'ComputerName')] [ValidateSet('CimSession', 'PSSession')] [string]$ConnectionPreference ) begin { $getRequestedLogInfoSplat = @{ } $TimeSpanSplat = @{ } switch ($PSBoundParameters.Keys) { 'DaysStale' { $TimeSpanSplat['Days'] = $DaysStale } 'HoursStale' { $TimeSpanSplat['Hours'] = $HoursStale } 'MinutesStale' { $TimeSpanSplat['Minutes'] = $MinutesStale } } $StaleTimeframe = New-TimeSpan @TimeSpanSplat switch ($LogFileName.EndsWith('.log')) { $false { $LogFileName = [string]::Format('{0}.log', $LogFileName) } } $MEMCMClientInstallLog = "$env:windir\ccmsetup\Logs\ccmsetup.log" } process { foreach ($Connection in (Get-Variable -Name $PSCmdlet.ParameterSetName -ValueOnly)) { $getConnectionInfoSplat = @{ $PSCmdlet.ParameterSetName = $Connection } switch ($PSBoundParameters.ContainsKey('ConnectionPreference')) { $true { $getConnectionInfoSplat['Prefer'] = $ConnectionPreference } } $ConnectionInfo = Get-CCMConnection @getConnectionInfoSplat $Computer = $ConnectionInfo.ComputerName $connectionSplat = $ConnectionInfo.connectionSplat $Result = [ordered]@{ } $Result['ComputerName'] = $Computer $Result['LogFileName'] = $LogFileName $Result['LogLastWriteTime'] = $null $Result['LogStale'] = $null $Result['CCMSetupLastWriteTime'] = $null $CCMLogDirectory = (Get-CCMLoggingConfiguration @connectionSplat).LogDirectory $LogFullPath = [string]::Join('\', $CCMLogDirectory, $LogFileName) Write-Verbose $([string]::Format('Checking {0} for activity', $LogFullPath)) $getRequestedLogInfoSplat['Query'] = [string]::Format('SELECT Readable, LastModified FROM CIM_DataFile WHERE Name = "{0}" OR Name = "{1}"', ($LogFullPath -replace "\\", "\\"), ($MEMCMClientInstallLog -replace "\\", "\\")) # 'Poke' the log by querying it once. Log files sometimes do not show an accurate LastModified time until they are accessed $null = switch ($Computer -eq $env:ComputerName) { $true { Get-CimInstance @getRequestedLogInfoSplat @connectionSplat } $false { Get-CCMCimInstance @getRequestedLogInfoSplat @connectionSplat } } $RequestedLogInfo = switch ($Computer -eq $env:ComputerName) { $true { Get-CimInstance @getRequestedLogInfoSplat @connectionSplat } $false { Get-CCMCimInstance @getRequestedLogInfoSplat @connectionSplat } } $RequestedLog = $RequestedLogInfo.Where({ $_.Name -eq $LogFullPath}) $MEMCMSetupLog = $RequestedLogInfo.Where({ $_.Name -eq $MEMCMClientInstallLog}) if ($null -ne $MEMCMSetupLog) { $Result['CCMSetupLastWriteTime'] = ([datetime]$dtmMEMCMClientInstallLogDate = $MEMCMSetupLog.LastModified) } if ($null -ne $RequestedLog) { $Result['LogLastWriteTime'] = ([datetime]$LogLastWriteTime = $RequestedLog.LastModified) $LastWriteDiff = New-TimeSpan -Start $LogLastWriteTime -End (Get-Date -format yyyy-MM-dd) if ($LastWriteDiff -gt $StaleTimeframe) { Write-Verbose $([string]::Format('{0} is not active', $LogFullPath)) Write-Verbose $([string]::Format('{0} last date modified is {1}', $LogFullPath, $LogDate)) Write-Verbose $([string]::Format("Current Date and Time is {0}", (Get-Date))) $LogStale = $true } else { Write-Verbose $([string]::Format('{0}.log is active', $LogFullPath)) $LogStale = $false } } elseif (-not $DisableCCMSetupFallback.IsPresent) { Write-Warning $([string]::Format('{0} not found; checking for recent ccmsetup activity', $LogFullPath)) if ($null -ne $MEMCMClientInstallLogInfo) { [int]$ClientInstallHours = (New-TimeSpan -Start $dtmMEMCMClientInstallLogDate -End (Get-Date)).TotalHours if ($ClientInstallHours -lt 24) { Write-Warning 'CCMSetup activity detected within last 24 hours - marking log as not stale' $LogStale = $false } else { Write-Warning 'CCMSetup activity not detected within last 24 hours - marking log as stale' $LogStale = $true } } else { Write-Warning $([string]::Format('CCMSetup.log not found in {0} - marking log as stale', $MEMCMClientInstallLog)) $LogStale = $true } } else { Write-Warning $([string]::Format('{0} not found', $LogFullPath)) $LogStale = 'File Not Found' } $Result['LogStale'] = $LogStale [pscustomobject]$Result } } } |