Backing up Event Logs on remote machines with PowerShell

Posted July 22nd @ 5:31 pm by aaron

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:  }

2 Comments

  1. aaron
    August 22, 2007 at 23:29

    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!

  2. DontBotherMeWithSpam
    August 22, 2007 at 23:29

    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.~

Leave a comment

OpenID Login

Standard Login

Options:

Size

Colors