Enterprise PowerShell Scripting Bootcamp
上QQ阅读APP看书,第一时间看更新

Script logging

As you create your enterprise script template, you will need to incorporate a logging mechanism. Logging allows you to capture script output, including informational, warning, and error messages. In typical logging scenarios, you will need to record script actions to either the event log, a log file, or a data collection file, like a Comma Separated Values (CSV) file. While PowerShell has a transcript which you can invoke, leveraging the start-transcript and stop-transcript cmdlets, it only allows you to record output to a single log file. This doesn't provide for writing to the event log or data collection files.

A popular logging mechanism is to create your own PowerShell logging function. This enables you to pass in parameters into the logging function to tell the script to either write to the event log, log file, or append data to the data collection file. It can also write the actions to the PowerShell window to view progress. This avoids having to write multiple lines of code each time you want something to be displayed in either the event log, log file, or the data collection file. You just need to call the logging function and pass in the required parameters, and it will write to the locations you specify.

Creating the logging files

To start, you will need to create the files for logging. Enterprises typically include both the server name and a date timestamp for log files. This allows for a clear identity of log files and execution times in large environments.

To create a time-stamped log file, you can perform the following:

$date = (Get-Date -format "yyyyMMddmmss")
$compname = $env:COMPUTERNAME
$logname = $compname + "_" + $date + "_ServerScanScript.log"
$scanlog = "c:\temp\logs\" + $logname 
new-item -path $scanlog -ItemType File -Force 

The output of creating the log file is shown in the following screenshot:

Creating the logging files

The preceding script displays how to create a .log file for script logging. To start, you leverage the Get-Date cmdlet with the -format parameter set to "yyyyMMddmmss" and place it into the $date variable. The $date variable will then have sequentially the four-digit year, two-digit month, two-digit day, two-digit minute, and two-digit seconds, from when the script was executed, contained in it. You then gather the computer name by leveraging the $env:COMPUTERNAME environment variable and placing the value into the $compname variable. You then combine the $compname variable with a plus symbol, an underscore for separation with a plus symbol, the date timestamp with a plus symbol, and log file name of "ServerScanScript.log", and store it in the $logname variable. You then create the full log path by specifying "c:\temp\logs\" with a plus symbol and the $logname directory and store it in the $scanlog variable. Finally, you leverage the new-item cmdlet with the -path parameter set to $scanlog, -ItemType set to File, and the -Force parameter. After execution, you will have a log file in c:\temp\logs\ with the filename of computername_datetimestamp_ServerScanScript.log.

To create a time-stamped data collection file in the format of a CSV file, you can perform the following:

$date = (Get-Date -format "yyyyMMddmmss")
$compname = $env:COMPUTERNAME
$logname = $compname + "_" + $date + "_ScanResults.csv"
$scanresults = "c:\temp\logs\" + $logname
new-item -path $scanresults -ItemType File -Force

# Add Content Headers to the CSV File
$csvheader = "ServerName, Classification, Other Data"
Add-Content -path $scanresults -Value $csvheader 

The output of creating the CSV file is shown in the following screenshot:

Creating the logging files

The preceding script displays how to create a data collection file in the format of a .csv file for script logging. To start, you leverage the Get-Date cmdlet with the -format parameter set to "yyyyMMddmmss" and place it into the $date variable. The $date variable will then have sequentially the four-digit year, two-digit month, two-digit day, two-digit minute, and two-digit seconds, from when the script was executed, contained in it. You then gather the computer name by leveraging the $env:COMPUTERNAME environment variable and placing the value into the $compname variable. You then combine the $compname variable with a plus symbol, an underscore for separation with a plus symbol, the date timestamp with a plus symbol, and log file name of _ScanResults.csv, and store it in the $logname variable. You then create the full log path by specifying "c:\temp\logs\" with a plus symbol and the $logname directory, and store it in the $scanresults variable. You will then leverage the new-item cmdlet with the -path parameter set to $scanlog, the -ItemType set to File, and the -Force parameter. After execution, you will have a .csv file in c:\temp\logs\ with the filename of computername_datetimestamp_ScanResults.csv.

Finally, to add the headers to the CSV file, you specify the header names separated by values as "ServerName, Classification, Other Data" and store it in the $csvheader variable. You then add the header to the CSV file by leveraging the Add-content cmdlet with the -path parameter set to $scanresults and the -value parameter set to $csvheader. The computername_datetimestamp_ScanResults.csv file will have the headers of ServerName, Classification, and Other Data.

When opening the CSV file in excel, it displays the headers as follows:

Creating the logging files

Tip

You may choose to optimize this code by not adding the computer name using the $compname variable. Alternatively, you can call the $env:COMPUTERNAME variable directly when setting the $logname variable. The script was written to display the progression of the log file string with the computer name, date timestamp, and script filename.

Creating a windows event log source

In the instance that you want to write to the Windows event log, one of the prerequisites is to register the script as an event source with the Windows event log system. This is done with the use of the New-EventLog cmdlet. You start by specifying the New-EventLog cmdlet, followed by the -LogName parameter followed by an event log name. You can either select your own event log or specify one of the built-in event logs, such as Application or system. Finally, you specify the -Source parameter with the name of the script you are executing.

You can only register the event log source once, which may lead to errors when executing a script multiple times on the same system. You can retrieve the event log sources by specifying the get-eventlog cmdlet with the -LogName parameter, and piping the results to Select-object Source -Unique. This is not very desirable as it requires the system to query all the log events to determine if a source is already registered. Since Windows systems have a large number of event logs and log events, the query is very CPU and memory intensive.

The fastest way to get around a duplicate event log source error message is to suppress the error message itself. This is done through leveraging the -ErrorAction parameter set to SilentlyContinue with the New-Eventlog cmdlet. The -ErrorAction parameter with SilentlyContinue specified will capture the error and silently continue with the script, suppressing the error messages.

To register a new event log source suppressing error messages, you can perform the following:

New-EventLog –LogName Application –Source "WindowsServerScanningScript" -ErrorAction SilentlyContinue

The output of the PowerShell window will look like the following:

Creating a windows event log source

The preceding script displays how to register a new event log source with the Windows event log system. You first start by using the New-EventLog cmdlet with the -LogName parameter set to Application. You then specify the -Source argument and set it to the script name of "WindowsServerScanningScript". Finally, you suppress error messages by using the -ErrorAction argument and setting it to SilentlyContinue. After executing this command, you will be able to write to the Application event log with the event source of WindowsServerScanningScript.

Tip

When you create an event log source, it provides the ability to use that source for logging of messages. You need to write something to that event log source to have it appear in the Application event log.

Note

The Windows event log cmdlets require that the PowerShell Window is Run as administrator. When testing this code, you will want to right-click the PowerShell icon and Run as administrator. If you are remotely executing this code, you may want to ensure the user credentials you are using have administrative rights to the system.

Creating the logging function

After creating the log files and registering the event log source, you are now able to create a custom logging function. The logging function will be flexible in that it can accept arguments for three types of data writes. These include event logs, log files, and the data collection .csv file.

The logging function is declared with functionlog {. You will then need to leverage a parameter block to accept arguments into the function. The parameter block is declared with param() and includes the variables $string, $scnlg, and $evntlg. $string will be the string value for the informational, warning, error, or data collection content. The $scnlg variable dictates if the content is written to the scan log file. The $evntlg variable dictates if the content is written to the event log.

To create the beginning of the logging function, type the following:

function log { param($string, $scnlg, $evntlg) 

Using a variation of the arguments with the logging function, you can write log information to different locations. You can set the following:

  • If $scnlg is set to Y and no value is specified for $evntlg, the script will only write to the scanning log file.
  • If $scnlg is set to N and $evntlg is set to Y, the script will write only to the event log.
  • If $scnlg is set to Y and $evntlg is set to Y, the script will write to both the scanning log file and the event log.
  • If no parameters other than $string are passed into the function, it will write the contents to the data collection CSV file.
  • In all cases, the logging function will leverage the write-host cmdlet with the contents of the $string variable. This will verbose print all the logging variations.

To create the full logging function, you can perform the following:

function log { param($string, $scnlg, $evntlg)
 # If Y is populated in the second position, add to log file.
 if ($scnlg -like "Y") { 
 Add-content -path $scanlog -Value $string
 }
 # If Y is populated in the third position, Log Item to Event Log As well
 if ($evntlg -like "Y") {
 write-eventlog -logname Application -source "WindowsServerScanningScript" -eventID 1000 -entrytype Information -message "$string"
 }
 # If there are no parameters specified, write to the data collection file (CSV)
 if (!$scnlg) {
 $content = "$env:COMPUTERNAME,$string"
 Add-Content -path $scanresults -Value $content
 }
 # Verbose Logging
 write-host $string
}

$date = Get-Date
log "Starting WindowsServerScanningScript at $date ..." "Y" "Y"
log "Writing a message to the Event Log Only." "N" "Y"
log "ScriptStart,$date"

The output of this script will look like the following screenshot:

Creating the logging function

This example displays how to create a logging function and log information to a log file, event log, and data collection CSV file. To start, you first declare function log {. You then accept three parameters into the function with the param($string, $scnlg, $evntlg) section of code. You then create an IF statement to determine if the $scnlg contents are -like "Y". If it returns true, you then execute the Add-content cmdlet with the -path parameter set to $scanlog, and the -Value parameter set to $string. The contents of the string will now be in the $scanlog file.

The second IF statement evaluates if the $evntlg contents are -like "Y". If it returns True, you then leverage the write-eventlog cmdlet with the -logname parameter set to Application. You also include the -source parameter set to "WindowsServerScanningScript", the -eventID parameter set to the event ID number of 1000, the -entrytype parameter set to Information, and the -message attribute set to $string. The contents of the string will now be in the Application event log under the source of WindowsServerScanningScript and the event ID of 1000.

Tip

Some Enterprise environments have monitoring solutions that can parse the event log and trigger actions based on the event IDs. As an alternative to passing in a "Y", you can pass in an event ID which can automatically trigger alerts with those monitoring systems. You would have to update the if statement to read if ($evntlg) {}, which will return True if data is contained in the variable. You can then set the -eventID parameter to $evntlg in the script, and you have the ability to change the event ID as needed.

The third if statement determines if there is any data in the $scnlg variable. This means the default action will be to write to the data collection CSV file. You leverage the if statement with an exclamation point preceding the $scnlg variable. This specifies if NOT True, or if there is not any data being passed in that variable, to proceed to the next steps. You then specify the environment variable of $env:COMPUTERNAME, followed by a comma, followed by the $string variable, and set the value in the $content variable. You then write the $content variable to the CSV file by leveraging the Add-content cmdlet with the -path parameter set to $scanresults, and the -value parameter set to $content.

The final step in the function is to display the content by using the write-host cmdlet with $string as the content.

To test the script, you then obtain the date timestamp by using the Get-Date cmdlet and setting the value in the $date variable. You then call the log function with the first argument of "Starting WindowsServerScanningScript at $date", the second argument of "Y", and the third argument of "Y". This writes the content in the first argument to both the log file and the event log.

The following displays the contents of the created log file:

Creating the logging function

The following displays the contents of the event log generated by the script:

Creating the logging function

You then call the log function again with the first argument of "Writing A Message to the Event Log Only.", with the second argument of "N", and the third argument set to "Y". This writes to the Application log with the source of WindowsServerScanningScript and the Event ID of 1000.

The following displays the contents of the event log generated by the script:

Creating the logging function

Finally, you call the log function with the first argument set to "Script,$date". No other arguments are specified with this function. The script will then write to the data collection CSV file with the following syntax—"computer name, ScriptStart, DateTimestamp".

The following displays the contents of the created data collection CSV file:

Creating the logging function