The other day, I demonstrated a fast &furious way to check eventlogs on the local computer. Unfortunately the Get-Eventlog cmdlet doesn’t have a remote computer option. To get event logs from remote computers, you need to use Get-WMIObject. Unfortunately,this requires a bit more work.
Here’s the code you would need to run:
$computer=”MyServer”
foreach ($log in (Get-WmiObject win32_nteventlogfile -computer $computer | Select-Object logfilename)) {
Write-Host -fore Green -back Black $log.logfilename ;
$query=”Select Eventcode,Eventtype,Logfile,Message,TimeGenerated from win32_NTLogEvent where logfile=’”+$log.logfilename+”’”;
Get-WmiObject -query $query -computer $computer | Select-Object EventCode,EventType,LogFile,Message,TimeGenerated -first 10 | More
}
Hardly a friendly one-liner. Parsing out the eventlog file names is a little more cumbersome:
Get-WmiObject win32_nteventlogfile -computer $computer | Select-Object logfilename
But once I have them, I can build a query:
$query=”Select Eventcode,Eventtype,Logfile,Message,TimeGenerated from win32_NTLogEvent where logfile=’”+$log.logfilename+”’”;
And then run Get-WMIObject, again selecting just the information I want and piping it to More:
Get-WmiObject -query $query -computer $computer | Select-Object EventCode,EventType,LogFile,Message,TimeGenerated -first 10 | More
I tried using the query directly, but the -query parameter didn’t like $log.logfilename. Since having a separate query worked, I moved on.
If you run this, it will look fine at first glance. But then you’ll realize you didn’ t see anything from Security eventlog. To access the Security Eventlog, you need to specify the Security Privilege. Unfortunately, the Get-WMIObject cmdlet has no such provision. The way around this is to use a WMISearcher object and enable privileges. You’ll also need to specify the remote computer in the scope.path:
$searcher=new-object WMISearcher
$searcher.Scope.Options.EnablePrivileges=1
$searcher.scope.path=\$computer\root\cimv2
Here’s what the modified code would look like:
$computer=”dc01”
$searcher=new-object WMISearcher
$searcher.Scope.Options.EnablePrivileges=1
$searcher.scope.path=”\$computer\root\cimv2”
foreach ($log in (Get-WmiObject win32_nteventlogfile -computer $computer | Select-Object logfilename)) {
Write-Host -fore Green -back Black $log.logfilename ;
$query=”Select Eventcode,Eventtype,Logfile,Message,TimeGenerated from win32_NTLogEvent where logfile=’”+$log.logfilename+”’”
$searcher.query=$query
$searcher.get() | Select-Object EventCode,EventType,LogFile,Message,TimeGenerated -first 5 | More
}
This should work, assuming you are running this with domain admin credentials. If you need to specify alternate credentials, then you not only have to pass them to the Get-WMIObject, but you also need to define them for the searcher object. But here’s the problem: the Get-WMIObject wants a secure string password, but the searcher object can only take plain text. I tried passing the results of ConvertFrom-SecureString as the searcher password, but no-go.
Eventually this is what I came up with:
$computer=”dc01”
$username=”mydomain\administrator”
#password has to be entered as clear text
$pw=Read-Host “Enter the password you’ll need”
#but credential requires secure string
$securepw=ConvertTo-SecureString $pw -AsPlainText -Force
$cred=new-object -typename System.Management.Automation.PSCredential -argumentlist $username,$securepw
$searcher=new-object WMISearcher
$searcher.Scope.Options.EnablePrivileges=1
$searcher.scope.path=”\$computer\root\cimv2”
$searcher.scope.options.username=$cred.Username
$searcher.scope.options.password=$pw
foreach ($log in (Get-WmiObject win32_nteventlogfile -computer $computer -credential $cred| Select-Object logfilename)) {
Write-Host -fore Green -back Black $log.logfilename ;
$query=”Select Eventcode,Eventtype,Logfile,Message,TimeGenerated from win32_NTLogEvent where logfile=’”+$log.logfilename+”’”
$searcher.query=$query
$searcher.get() | Select-Object EventCode,EventType,LogFile,Message,TimeGenerated -first 5 | More
}
As you can see, hardly a fast & furious solution and far from elegant. The password you enter will be displayed as plain text. But this gets the job done. Although, if your logs are too big you might get a quota violation. I’ll have to deal with that later.
This task would be a whole lot easier if Get-Eventlog could query remote computers, or Get-WMIObject could set privileges. Actually, it would be great if both cmdlets could be enhanced to povide these features.
I agree the cmdlets should either query local or remote machines. I can’t wait until these two commandlets have these available.
Cmdlets will probably never have the ability to query a remote machine – that’s now how PowerShell will implement remoting. Instead, you’ll essentially run a new instance of the shell and tell IT to run on the remote machine, and then execute whatever you want. That way remoting doesn’t have to be built into individual cmdlets; it’s built into the overall shell instead so EVERY cmdlet is remotable.
Speaking on the Fast and the Furious:
I have expiremented with both the WMI and .NET methods for manipulating remote server event logs.
My site has a larged mixed OS Windows environment of approximatly 1,500 servers of various ages, makes, models, and network connections.
My question:
Some servers .Net/WMI inquiries are really really slow. Do you know if the logs are imported accross the network and then filtered and parsed by the logic on the powershell server or is there a way to make the remote server do the filtering and parsing and just send back the items of interest? How does this work.
# The .Net Method
# $d=get-date
# $FQDNservername="MyserverName"
# $logs = [System.Diagnostics.EventLog]::GetEventLogs($FQDNservername)
# $logs
# for ($i=0; $i -lt $logs.length; $i++) {If ($logs[$i].logdisplayname -eq "System") {$LogIndex=$I}}
# $LogName = $logs[$LogIndex].logdisplayname
# "LogIndex = $LogIndex to $LogName Event Log" | Out-host
# Write-host $logs[$logIndex].logdisplayname EventLog for Server $logs[$LogIndex].machinename -backgroundcolor green -foregroundcolor black
# $B = $logs[$LogIndex].entries | where {($_.EventID -eq "20048") -and ($_.TimeWritten -ge $D.adddays(-7))}
This command:
# The WMI Method
$d=get-date
$BeginDate=[System.Management.ManagementDateTimeConverter]::ToDMTFDateTime($d.AddDays(-7))
$B = get-wmiobject –class win32_ntlogevent –computerName $FQDNservername -credential $Credentials | Where-object { $_.logfile -eq "system" -and ($_.eventcode –eq “20048" -and $_.TimeWritten -ge $BeginDate) } | select-object Logfile,EventCode,TimeGenerated,TimeWritten,Message
Does not process any faster than these commands.
$Credentials=Get-Credential("Mydomain\DomainAdmin")
$d=get-date
$BeginDate=[System.Management.ManagementDateTimeConverter]::ToDMTFDateTime($d.AddDays(-7))
$FQDNservername="MyserverName"
$A = get-wmiobject –class win32_ntlogevent –computerName $FQDNservername -credential $Credentials
$logs = $A | Where-object { $_.logfile -eq "system"}
Write-host "System event log acquired for Server $FQDNservername" -backgroundcolor green -foregroundcolor black
$BeginDate=[System.Management.ManagementDateTimeConverter]::ToDMTFDateTime($d.AddDays(-7))
$B= $logs | where-object { $_.eventcode –eq “20048" -and $_.TimeWritten -ge $BeginDate }
$results = $B | select-object TimeGenerated,Message
# $T = [System.Management.ManagementDateTimeConverter]::ToDateTime($results[1].timegenerated)
I mean does any part of the query occur on the remote host or does all the query/parsing/selecting occur on the Powershell host after all or some of the remote event logs have been shipped accross the network? Is there a way to optimize this for a slow network?
-Kevin
I know for WMI, and I don’t think the .NET approach is any different, but no processing happens on the remote host. When you run an event log query, the information is transferred over the wire to the you. Your system does alll the heavy lifting. For a LAN, that’s usually not too bad. But over a WAN, it could get ugly. We really need better remote processing but it’s not here yet. One thing you could do to mitigate is keep your event log sizes manageable. Querying a 50MB file will certainly take longer than a 10MB file.
If you want to discuss this further, please start a discussion topic in the PowerShell forum at ScriptingAnswers.com.