Backing up Event Logs on remote machines with PowerShell
A really good friend of mine who also happens to be in the IT department at my company came and asked me about helping him with a script to back up (but not clear) event logs from remote servers. He had a script (the standard VB script from Microsoft’s website) that would back up and clear the local event log, but not a remote one. Of course the first word out of my mouth was “PowerShell” (to which he rolled his eyes).
Well, it was a little more complicated than I had hoped since BackupEventLog is not exposed by the Win32_NTEventlogFile WMI object. But Jeffrey Snover and the PowerShell Team blog came to my rescue with a great post on using P/Invoke inside PowerShell. There’s some really useful stuff posted on that blog!
I put this script in a file called “BackupEventLogs.ps1” and call it like this:
PS> .\BackupEventLogs.ps1 servers.txt “C:\EventLogBackups”
An example of “servers.txt” is the following:
SERVER1 | C:\EventLogTemp | EventLog
SERVER2 | C:\EventLogTemp | EventLog
Where “SERVERx” is the server name, “C:\EventLogTemp” is an existing local directory on the machine where the event log gets placed, and “EventLog” is the name of the network share pointing to “C:\EventLogTemp” in order to copy it locally.
I’m snipping the copied script from the PowerShell blog post, but that aside here’s the final (but not refined) script:
1: param
2: (
3: [string] $listFileName = "servers.txt",
4: [string] $localDir = $(throw "You must specify a destination for backed up logs")
5: )
6:
7: #######################################################################
8: # Compile some specified code into an assembly
9: #######################################################################
10: function Compile-Csharp ([string] $code, $FrameworkVersion="v2.0.50727",
11: [Array]$References)
12: {
13: # ... Snip ... #
14: }
15:
16: $code = @'
17: using System;
18: using System.Runtime.InteropServices;
19:
20: namespace ININ.IT.Scripting
21: {
22: public class EventLogExtensions
23: {
24: [DllImport("advapi32.dll")]
25: public static extern IntPtr OpenEventLog(string serverName, string sourceLogName);
26: [DllImport("advapi32.dll")]
27: public static extern bool CloseEventLog(IntPtr hEventLog);
28: [DllImport("advapi32.dll")]
29: internal static extern bool BackupEventLog(IntPtr hEventLog, string backupFile);
30:
31: public static void Backup(string serverName, string logFile, string backupFile)
32: {
33: IntPtr eventLog = OpenEventLog(serverName, logFile);
34: if (eventLog != null && eventLog != IntPtr.Zero)
35: {
36: BackupEventLog(eventLog, backupFile);
37: CloseEventLog(eventLog);
38: }
39: else
40: {
41: throw new Exception("Unable to open event log for: " + serverName);
42: }
43: }
44: }
45: }
46: '@
47:
48: ##################################################################
49: # So now we compile the code and use .NET object access to run it.
50: ##################################################################
51: compile-CSharp $code
52:
53: $fileInfo = Get-Content $listFileName
54:
55: if ($fileInfo -eq $null) { throw "No servers specified" }
56:
57: # Back up all the event logs and copy then to the local path
58: $fileInfo |
59: foreach {
60: $items = $_.split("|")
61: # I'm having trouble getting $items[0] to expand within a string, so I'm assigning each one to it's own variable (yuk)
62: $serverName = $items[0].trim()
63: $remoteDir = $items[1].trim()
64: $remoteShareName = $items[2].trim()
65: [ININ.IT.Scripting.EventLogExtensions]::Backup("\$serverName", "Application", "$remoteDir$serverName-Application.evt")
66: Copy-Item "\$serverName$remoteShareName$serverName-Application.evt" -destination $localDir
67: }
August 22nd, 2007 at 11:29 pm
Rock on! Thanks!
BTW, I used your blog as a resource a lot for the little “syntax-y” things I was too lazy to look in the docs for.
So double-thanks!
August 22nd, 2007 at 11:29 pm
There is a better way (IMHO) to call Win32 API methods on Lee Holme’s blog page at http://www.leeholmes.com/blog/GetTheOwnerOfAProcessInPowerShellPInvokeAndRefOutParameters.aspx
If you look at how “Invoke-Win32″ function is used in his script, you will be able to modify your script a bit cleaner since the one you have posted is, yet, to be cleaned
>I’m having trouble getting $items[0] to expand within a string
You can do the following for variable expansion inside the string.
Instead of
>$serverName = $items[0].trim()
> …
> “$serverName”,…
You can do
… snip …::Backup(“$($item[0].trim())”, …
There was a NG(Newsgroup) post on microsoft.public.windows.powershell
http://www.microsoft.com/communities/newsgroups/list/en-us/default.aspx?dg=microsoft.public.windows.powershell&mid=99a62264-cf9f-491b-97cf-5704316c101b
about exact problem you were having as well as JS(Jeffrey Snover, the Architect of PowerShell and Aspen) on PowerShell team blog site, http://blogs.msdn.com/powershell/archive/2006/07/15/Variable_expansion_in_strings_and_herestrings.aspx
Have fun.~