Category Quick Fixes

Event Log Properties – PowerShell

One of the best uses of PowerShell in my experience is auditing the Event Logs, and then emailing a HTML report highlighting issues. The issue I have had with that, is once I know what Event ID I want from what Event Log, is knowing what properties exactly to extract, so I can see the Person who did it, on what Machine it happened, what time the event happened. There are undoubtedly a bunch of things you might want, and the issue is many of the real gritty details are hidden in the Properties[x].Value component.

Trickier still the Account name might be Properties[6].Value in one Event Log, and then in Properties[4].Value in another.

Today we decided we wanted to add Password Changes for the Admin and Services accounts to our Audit script (Event ID 4723 and 4724 if done by the affected account, or my another account)

This script looks at roughly 20 AD Event IDs in the Security Log, so I needed to look inside or trawl the internet (once again) for what was stored where, so I could do a foreach ($event in $global:EventLogs), that would extract the accounts involved and some other details.

The Script

I’ve used my Base Menu Script (read about it here), so there is a bunch of structure in there to make it dynamic, smooth and easy to read and edit, but in the guts there are two key components:

Reading an Event Log

#------------------------------------------------------------------------------------------------
#  Do the Scan of the Targeted Event Log
#------------------------------------------------------------------------------------------------
Function Do-Scan{

    cls
    #Converts the Hours back in to Milliseconds back
    $GoTime  = $global:HoursBack*3600000
    #Create XML Filter
    $FilterSEC = '<QueryList>
                    <Query Id="0" Path="'
    $FilterSEC += $global:Events
    $FilterSEC += '">
                        <Select Path="'
    $FilterSEC += $global:Events
    $FilterSEC += '">*[System[(EventID='
    $FilterSEC += $global:EventID
    $FilterSEC += ') and TimeCreated[timediff(@SystemTime) <= ' 
    $FilterSEC += $GoTime         
    $FilterSEC += ']]]</Select>
                    </Query>
                    </QueryList>'
	
    $global:eventslog = Get-WinEvent -ComputerName $global:LogServer -FilterXml $FilterSEC	

   $ErrorActionPreference= 'continue' 
   Show-Results
    
        
}

I always do my Event queries as Get-WinEvent and -FilterXML; I find them to be the quickest to run, and you really want to lock down that filter, as you may want to target all the DCs, and when doing that you really want that filter to be targeting only the Events you actually want.

You’ll notice the $FilterSec is made up of other components that you can change from the menu as shown below:

Menu – Showing what can be changed.

Outputting all the Events Properties

#------------------------------------------------------------------------------------------------
#  Show Results in two Formats
#------------------------------------------------------------------------------------------------
Function Show-Results{

    Write-Host "     EVENT Property Extractor"  -ForegroundColor Cyan
    Write-Host "   ---------------------------------------------"
    Write-Host "  "
    Write-Host "   Source: " $global:LogServer
    Write-Host "   Event Log: " $global:Events
    Write-Host "   Event ID:  " $global:EventID
    Write-Host "   Hours Back:" $global:HoursBack

    $global:eventslog | select-object -First 1 | fl -Property * | Out-host

    $once=$true
    foreach ($e in $global:eventslog) {
        if ($once -eq $true){
            write-host "Properties[0].Value   :"$e.Properties[0].Value
            write-host "Properties[1].Value   :"$e.Properties[1].Value
            write-host "Properties[2].Value   :"$e.Properties[2].Value
            write-host "Properties[3].Value   :"$e.Properties[3].Value
            write-host "Properties[4].Value   :"$e.Properties[4].Value
            write-host "Properties[5].Value   :"$e.Properties[5].Value
            write-host "Properties[6].Value   :"$e.Properties[6].Value
            write-host "Properties[7].Value   :"$e.Properties[7].Value
            write-host "Properties[8].Value   :"$e.Properties[8].Value
            write-host "Properties[9].Value   :"$e.Properties[9].Value

            write-host "Properties[10].Value   :"$e.Properties[10].Value
            write-host "Properties[11].Value   :"$e.Properties[11].Value
            write-host "Properties[12].Value   :"$e.Properties[12].Value
            write-host "Properties[13].Value   :"$e.Properties[13].Value
            write-host "Properties[14].Value   :"$e.Properties[14].Value
            write-host "Properties[15].Value   :"$e.Properties[15].Value
            write-host "Properties[16].Value   :"$e.Properties[16].Value
            write-host "Properties[17].Value   :"$e.Properties[17].Value
            write-host "Properties[18].Value   :"$e.Properties[18].Value
            write-host "Properties[19].Value   :"$e.Properties[19].Value

            write-host "Properties[20].Value   :"$e.Properties[20].Value
            write-host "Properties[21].Value   :"$e.Properties[21].Value
            write-host "Properties[22].Value   :"$e.Properties[22].Value
            write-host "Properties[23].Value   :"$e.Properties[23].Value
            write-host "Properties[24].Value   :"$e.Properties[24].Value
            write-host "Properties[25].Value   :"$e.Properties[25].Value
            write-host "Properties[26].Value   :"$e.Properties[26].Value
            write-host "Properties[27].Value   :"$e.Properties[27].Value
            write-host "Properties[28].Value   :"$e.Properties[28].Value
            write-host "Properties[29].Value   :"$e.Properties[29].Value
        }
        $once=$false
    }
     Make-Menu -MenuTitle "Do Another?" `
    -Option1 "Yes" -Function1 "Do-Welcome" `
    -Option2 "No" -Function2 "Get-Quiting" 
}

Finally the results, which we dump the default view, and then go through 30 other possible values that could be buried in the Message part of the Event, and are often the key information you are looking for.

What it Doesn’t Do

Because this is just a tool that you will use to help you write actual functional scripts, I didn’t make it perfectly polished, or with error handling. Here’s what is doesn’t do:

  • Check if a Event ID of that type exists.
  • Restrict the Event Log selection to only valid sources.
  • Allow you to enter a Log Server source.
  • Allow you to move through a series of the Events with the full display.

Remember its a quick tool just for helping me find the properties of (in my specific case today), who’s password was changed, and who did the changing.

Download the Script

EventLog_Props.ps1 https://github.com/Works4Me-Info/PS_EventLog-Properties

Below is an example of the kind of output you would expect to see. I’ve put it at the bottom here, so it doesn’t hide the Download option.

Example Output

Personalised Email Templates – VBA

Are you sending the same email over and over again, with just a few alterations? I know I do, and one of the easiest ways is to open an oft template and then change some words.

Adding a Macro

You can use this post to see how to add macros, and how to enable a button for it:

The Solution

This is a simple two part solution:

1. The Email Template

Which has [TARGET] words that will be replaced from questions that are asked by input boxes.

2. The VBA Macro

This will open the Macro, ask you some Inputs, and then replace the [TARGET] words with what you entered in the inputs.

The Template

This is the easy part, just build an email then opt to Save As the message, as a template (*.oft). This is the example of the one used by the code. You’ll see three uses of [Gift] and one of the [Gifter] target words.

I’ve used a word inside the square brackers [TARGET]

The Code

Theres a lot you can do here, but I’ve opted for the basics – targeting words in the subject line and the body of the message.

You run the Coffee_Thanks() as a Quick Access button, which for each replacement the script calls the replaceText() sub.

Sub Coffee_Thanks()
    
    '+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-'
    '  VARIABLES  -------------------------------------------------------------------------------'
    '+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-'
        'DATA----------------------------------------  
        Dim CoffeeAmount As String 
        Dim personName As String 
        'TEMPLATE------------------------------------ 
        Dim newItem As Outlook.MailItem  
        'REPLACEMENTS-------------------------------- 
        Dim wdDoc As Object 
        Dim olItem As Object 
        Dim olInsp As Outlook.inspector 
    '+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-'




    '+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-'
    '  GATHER INFO ------------------------------------------------------------------------------'
    '+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-'
       ' Gather additional information-------------- 
        CoffeeAmount = InputBox("Coffee Gift in $:")    
        personName = InputBox("Recipients name:")
    '+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-'
   
    
    
    
    '+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-'
    '  OPEN TEMPLATE ----------------------------------------------------------------------------'
    '+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-'
        ' open Template------------------------------  
        Set newItem = Application.CreateItemFromTemplate("D:\Templates\Outlook\Coffee_Thanks.oft")
        newItem.Display
    '+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-'
    
    
    
    '+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-'
    '  TEXT REPLACEMENTS ------------------------------------------------------------------------'
    '+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-'
       ' Set up for changes-------------------------  
		   ' Word Swaps in Subject line-----------------  
				Set olInsp = Application.ActiveInspector  
				Set olItem = olInsp.currentItem  
			 ' Word swaps in Body--------------------------
				Set wdDoc = newItem.GetInspector.WordEditor 

		   ' Replace all placeholders-------------------   
        olItem.Subject = Replace(olItem.Subject, "[Gift]", CoffeeAmount) 
        Call replaceText(wdDoc, "[Gifter]", personName)
        Call replaceText(wdDoc, "[Gift]", CoffeeAmount)
    '+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-'
    
End Sub

Sub replaceText(wdDoc As Object, findText As String, replaceText As String)
	'+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-'
    '+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-'
    ' Repeatable loop to replace words in the body, without altering
    ' the HTML formatting.
	'+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-'
    With wdDoc.Content.Find
        .Text = findText
        .Replacement.Text = replaceText
        .Execute Replace:=2 ' 2 = Replace all
    End With
End Sub

NOTE: I’ve saved my oft template to “D:\Templates\Outlook\Coffee_Thanks.oft”, you will need to change the code to point to where ever you have saved the template.

There’s a mountain different ways to use this, and you can do various other things like:

  • Calculate the time of day to tweak the greating to a good morning, or afternoon.
  • Find out when the next weekend is, because you’re using this to warn about patching.
  • Add people to the CC or BC fields.
  • Use input choices to add different content.

Download the Code

WordReplacement.vb https://github.com/Works4Me-Info/Outlook-Macros