Archive

Archive for January, 2012

Backing Up Event Logs using Powershell

January 31, 2012 5 comments

I was recently asked to create a script that would backup certain event logs ( Application & Security ) to it’s native .evt format and then clear all events from the corresponding logs once complete.  This seemed simple enough although I didn’t recall seeing any parameters in either Get-WinEvent or any of the *-Eventlog cmdlets that provided this functionality.  Then I remembered that when something can’t be done using object specific cmdlet the next possible option is to explore the Win32_* classess.   So I used Get-WMIObject to query possible Win32_* classes that referenced Event Log.

Get-WMIObject Win32_*event* -List

The query produced the following results:

So the question now was which Win32 class to choose from.   I  narrowed it down to the Win32_NTEvent* classes and after some further examination determined that Win32_NTEventLogFile had a method called BackupEventLog.  I was able to make this determination by using Get-Member on the class.

Get-WMIObject Win32_NTEventLogFile | Get-Member

This query displayed all Properties and Methods of the Event Logs.  I’ve filtered the results to display only the first few Methods

The BackupEventLogFile method accepts one overload of System.String type which will be the name of the backup log file with an .evt extension.  The files were going to be backed up daily and then the Event Logs cleared of all events  so I needed to make sure the backup log files had unique names and decided to include the current date in the event log name.  I also needed to use a Foreach loop so as to run the code on several Event Logs in sequence.   I also included the following parameters to make the function more versitile:

 Param(
      $Computername = $ENV:COMPUTERNAME,
      [array]$EventLogs = @("application","security"),
      $BackupFolder = "C:\BackupEventLogs\"
      )

Logic was also added to create the $BackupFolder if it didn’t exist.

If(!( Test-Path $BackupFolder )) { New-Item $BackupFolder -Type Directory }

I called the function Clear-EventLogs and below is the complete script.

Function Clear-Eventlogs {            

 Param(
  $Computername = $ENV:COMPUTERNAME,
  [array]$EventLogs = @("application","security"),
  $BackupFolder = "C:\BackupEventLogs\"
  )            

 Foreach ( $i in $EventLogs ) {
 If(!( Test-Path $BackupFolder )) { New-Item $BackupFolder -Type Directory }
 $eventlog="c:\BackupEventLogs\$i" + (Get-Date).tostring("yyyyMMdd") + ".evt"
 (get-wmiobject win32_nteventlogfile -ComputerName $computername |
  Where {$_.logfilename -eq "$i"}).backupeventlog($eventlog)            

 Clear-EventLog -LogName $i            

 }# end Foreach            

}#end function            

Clear-Eventlogs

The results of running the script are the following log files:

Categories: Powershell Tags: ,

Creating Hyper-V Symbolic Links using Powershell

January 31, 2012 1 comment

As an MCT I’m required to download preconfigured VM’s and supporting VHD’s from Microsoft which are then imported into base images running Server 2008 R2 and HyperV.  Prior to importing the VM’s, symbolic links need to be created in each VM’s root Virutal Hard Disk folder.  Symbolic links are shortcuts that are associated with base or middle tier VHD’s.  Microsoft even includes a .bat file that automates their creation.  An example of one of these .bat files is below:

@ECHO OFF
@ECHO Creating Symlinks necessary to import VM’s
rem ************  to create Symlink for Base Image
rem ************substitute VHDName.vhd with the name of the base image needed
mklink “Path:\Source.vhd” “Path:\Base.vhd”
pause

So it occurred to me that if a .bat file could automate the creation of one or more symbolic links, then perhaps a Powershell script could automate the execution of one or more individual .bat files.  Creating these symbolic links is a repetitive act of having to browse to each root VM’s folder, execute the .bat file and then close the corresponding cmd prompt.  This is surely another instance where time could be better spent elsewhere and so I turn to my old friend Powershell for a script to automate this mundane task.

The first step is define the path to the root VM folders.

$path = "C:\Program Files\Microsoft Learning\6419\Drives\"

Now we run Get-ChildItem on the folder using the -Include parameter to filter for .bat files only and then -Recurse to browse sub folders and files.   These results are then placed into the $vhds array variable.

$vhds = Get-ChildItem $path -Include *.bat -Recurse

The next step is to execute the .bat files by piping the $vhds array containing the required .bat files to Start-Process.  One of the problems I encountered was that, although Start-Process was able to execute the .bat files it wasn’t passing it’s results down the pipeline.  Most cmdlets do return output but on occasion certain cmdlets needs to be encouraged to do this using the -PassThru  parameter.

$vhds | %{ start-process "$_" -PassThru |

These results are then pipelined to Foreach-Object where I found it necessary to pause the script and allow the .bat file to properly execute using Start-Sleep 1.  Without the pause there is a quick blip on the screen and then the .bat file exists.  I remember reading a script in a recent blog article by Greg Caporale regarding printing .pdf’s where he incorporated Start-Sleep ( or it’s alias sleep ) to provide enough time for the .pdf files to print before closing Adobe Reader.   The Foreach-Object also passes down the pipline each individually open cmd process to Stop-Process which exits the open cmd prompt.

$vhds | %{ Start-Process "$_" -PassThru |
        %{ Start-Sleep 1 ; $_} | Stop-Process }

I then wrapped the whole thing into a function called it Import-SymbolicLinks and defined a $Path parameter to make the script that much more flexible by accepting parameter input. Below if the final script.

Function Create-SymbolicLinks {            

 Param(
  $path = "C:\Program Files\Microsoft Learning\6419\Drives\"
  )            

 $vhds = Get-ChildItem $path -Include *.bat -Recurse
 $vhds | %{ Start-Process "$_" -PassThru |
            %{ Start-Sleep 1 ; $_} | Stop-Process }
 }            

Create-SymbolicLinks

The obvious next step is to script the import of these VM’s as well as create snapshots.

Categories: Powershell Tags: ,

Query Security Log Using Powershell

January 13, 2012 7 comments

I’ve just completed a script that will parse the Windows Security Event log for Event ID’s of type 4624 (user logons).    Once the events have been retrieved the script then creates and outputs a  custom object populated with the following properties:

  • Account Name
  • DateTime
  • Type  ( Interactive,Network,Unlock)

The script is composed of 2 functions:

  • Find-Matches
  • Query-SecurityLog

Query-SecurityLog is the main function and is responsible for creating and outputing the custom object.  Find-Matches is a helper function used to parse the event log message property for the following pattern: “Account Name:      SomeUserName”.    Prior to incorporating the Find-Matches function I had used the -Match operator but then realized that pattern was referenced twice  in the message and that the -Match operator returns only the first matching expression.  So, if you need to find more than one occurrence of a pattern in raw text, you have to switch over to the RegEx object underlying the -Match operator and use it directly.

This left me with 2 possible option that could be used to discover one or more matching values:

  • Select-String (as used in Find-Matches)
  • [regex]

The Find-Matches function was something I already had in my repository of scripts so I went that route, however after reading Chapter 13: Text and Regular Expression in the Powershell.com ebook I realized this could of been done with less code by using [regex].

$Pattern = [regex]"(?i)\baccount name:\s+\w+\b"
$Content = $Pattern.matches($i.message) | select -prop value | %{$_.value}

The example below demonstrates how Select-String is used in the Find-Matches function to output all matching patterns:


I then used an if\else statemtment block to determine  how many matches were returned and then set the $account parameter accordingly.

if($content.Count -eq 2) {
$account = $content[1]} else {$account =  $content }

I was also suprised to see just how many non-user account logon events were recorded and decided to remove any events that referenced the accounts below:

  • System
  • IUSER
  • LOCAL
  • NETWORK
  • $ENV:Computername

As well as filter the events based on the following Logon Types and assign the $logontype variable accordingly:

  • Interactive (2)
  • Network (3)
  • Computer Unlocked (7)

This was done using the following code:

$notmatch = "System|IUSR|LOCAL|NETWORK"
if($account -notmatch $notmatch)  {                        

    if($i.Message | Select-String -Pattern "Logon Type:\s+[2]") {
    $logontype = "Interactive" }
    if($i.Message | Select-String -Pattern "Logon Type:\s+[3]") {
    $logontype = "Network" }
    if($i.Message | Select-String -Pattern "Logon Type:\s+[7]") {
    $logontype = "Computer Unlocked" }
    }

It was then just a matter of creating and populating the custom object.  There are 2 ways to populate an object once created.  One way is to create the object and then use the Add-Member cmdlet to populate it and the other is to use the -Property property along with splatting ( @{} ).

  • $Obj | Add-Member -Noteproperty NameOfProperty  ValueOfProperty
  • -Property @{ NameOfProperty = “Value” }

I choose to go with the Splatting option as it requires less code.

$obj = New-Object PSObject -Property @{
       User = $user
       Date = $Date
       LogonType = $LogonType
       }

The end result was the following code.

Function Find-Matches {                        

 Param($Pattern)
 Process {
  $_ | Select-String -pattern $Pattern -AllMatches |
   select -ExpandProperty matches |
   select -ExpandProperty value
  }
 }                        

Function Query-SecurityLog {               

    Param(
        [int]$MaxEvents,
        [array]$global:Users = @(),
        [string]$Comp = $env:computername,
        $notmatch = "System|IUSR|LOCAL|NETWORK"            

        )                 

    if($MaxEvents)
    { $events = Get-WinEvent -LogName security -MaxEvents $MaxEvents |
    Where-Object{$_.id -eq "4624"}
    } Else {  $events = Get-WinEvent -LogName security  |
    Where-Object{$_.id -eq "4624"}}            

    Foreach($i in $events) {                        

    $content = $i.message| Find-Matches -Pattern "account name:\s+\w+"
    if($content.Count -eq 2) {
    $account = $content[1]} else {$account =  $content }
    $account = (($account -split ":")[1]) -replace "\s+",""                        

    if($account -notmatch $notmatch) {                        

        if($i.Message | Select-String -Pattern "Logon Type:\s+[2]") {
        $logontype = "Interactive" }
        if($i.Message | Select-String -Pattern "Logon Type:\s+[3]") {
        $logontype = "Network" }
        if($i.Message | Select-String -Pattern "Logon Type:\s+[7]") {
        $logontype = "Computer Unlocked" }                        

   $user = $account
   $Date = $i.TimeCreated
   $obj = New-Object PSObject -Property @{
       User = $user
       Date = $Date
       LogonType = $LogonType
       }            

  $Global:Users += $Obj                        

       }
    }                        

 write-output $Global:Users | Select User,Date,LogonType |
             Sort Date -Descending | Format-Table -Auto
}                        

Query-SecurityLog -MaxEvents 1000

The output of Query-SecurityLog  is:

Categories: Powershell Tags:

Powershell Objects and .PS1XML Files

January 9, 2012 1 comment

Anyone who uses powershell on a regular basis knows that the output of any powershell cmdlet is of an object type.  Powershell produces objects. For example let’s look at the objects produced by two cmdlets that essentially do the same thing: Get-Eventlog and Get-WinEvent.   The below example uses the GetType() method to display the object type for an eventlog.

(Get-Eventlog -List | Select -First 1).Gettype() | FT -auto
(Get-WinEvent -Listlog Application).Gettype() | FT -auto

Both produce different objects, Eventlog and EventLogConfiguration.  The output of these objects is associated with a powershell formating file (.ps1xml) which defines the properties they will display.

The example below shows the output as it pertains to Get-Eventlog and Get-Winevent.

(Get-Eventlog -List | Select -First 1)
(Get-WinEvent -Listlog Application)

Although they both reference the same event log (Application) they are configured to display different properties, even though some of them reference the same values, like Log and LogName or Entries and RecordCount.

I then was curious which formatting files were associate with each object type, so I decided to view which .ps1xml files existed and ran the following command to browse the $pshome directory and filter for only *.ps1xml files.

dir $pshome\* -include *.ps1xml

My next step was to parse these files using Select-String  for references to the object types (Eventlog and EventLogConfiguration) but decided that the process should be automated and created a function that would create a custom object with the properties of my choosing.   This would also be useful to demo for the workshop I’m putting together called “Using Powershell to View Events and Event Logs” (not sure I did the title so it may change shortly).

Anyway, the result of all this was the creation of the Get-EventLogBaseObjects function.

Function Get-EventLogBaseObjects {
 $Array = "Get-EventLog","Get-WinEvent"
 $global:newarray =@()                                                

     Switch ($Array) {                                                

 "Get-Eventlog" {
    $GetEventLog = (get-eventlog -list | Select -first 1).gettype()
    $XMLFile = Select-String -Path $PSHOME\*.ps1xml -Pattern $GetEventlog |
        Select -First 1
    $Obj = New-Object PSObject -Property @{
            Cmdlet = "Get-Eventlog"
            SystemType = $GetEventLog.UnderlyingSystemType
            XMLFile = $XMLFile.filename
            }
    $global:newarray += $obj
    }                                    

 "Get-WinEvent" {
    $GetWinEvent = (Get-WinEvent -Listlog Application).gettype()
    $XMLFile = Select-String -Path $PSHOME\*.ps1xml -Pattern $GetWinEvent |
        Select -First 1
    $Obj = New-Object PSObject -Property @{
            Cmdlet = "Get-WinEvent"
            SystemType = $GetWinEvent.UnderlyingSystemType
            XMLFile = $XMLFile.filename
            }
    $global:newarray += $obj
     }
 }
 write-output $global:NewArray | Select Cmdlet,SytemType,XMLFile |
        Format-Table -AutoSize
}                        

Get-EventLogBaseObjects
Categories: Powershell Tags: ,

Determine Pipeline Input

January 7, 2012 Leave a comment

There was a recent post on the Powershell Forums titled Finding Function Name in which the user wanted to know if there was anyway to determine which cmdlet or function was being used to send input down the pipeline.  In order to assist I first needed to do some research as I was unsure how to make this determination.  It just so happened that the first response was by Kazun (PS MVP..enough said.. ) and he suggested using $MyInvocation.Line. 

So upon doing a google search I came across the following article PowerShell Deep Dive: Using $MyInvocation by Kirk Munro which went into great detail on how to use the $MyInvocation variable to make a similiar distinction.  From that article I came up with  the following script to demo $MyInvocation.  It includes 4 functions.  The one that anaylzes the pipeline input is Out-Name.   Here is the script…

Function Get-Svc {            
      Get-Service | select -First 2            
    }            
Function Get-Proc {            
      Get-Process | Select -First 2            
    }            
Function Get-File {            
      GCI c:\ | Select -First 2            
    }            

Function Out-Name {            
    param($name)            

    Begin {             
       $global:Invocation = $MyInvocation.line             
    }            

    Process{             
       if( $global:Invocation -match "Get-Svc" ) {            
       $_.name             
       }            
       Elseif ( $global:Invocation -match "Get-Proc" ) {            
       $_.vm            
       }             
       Else { Write-Host "Get-Svc was not used" }                   
    }            
  }            

Get-Svc | out-Name            
#Get-Proc | out-name            
#Get-File | Out-Name
 

The script was created soley to demo $MyInvocation but could be altered (as with any script) to make it fit your needs.

Categories: Powershell Tags: