How to Log built-in PowerShell commands (non-PSADT commands)

How to Log built-in PowerShell commands (non-PSADT commands)

Why use PSADT’s Set-Registry function instead of PowerShell’s own Set-item cmdlet?
Why use PSADT’s copy-file function instead of powerShell’s own copy-item cmdlet?
Answer: Logging, error handling and handling of special cases.

But what about the Non-PSADT functions? You know, the built-in stuff.
Here are 2 methods:

1. Redirection

This the quick and dirty method that I use when I can:

Remove-NetFirewallRule -DisplayName $RuleName -Verbose *>&1 | Out-String | write-log

-Verbose switch is to make sure Write-log has something to write. Otherwise Write-log will blow up and take to the whole script with it.
Out-String removes unprintable characters and formatting that might make Write-log blow up and take to the whole script with it.
It works most times but some fancy cmdlets still don’t report errors like Set-Service .

2. Cmdlet variables

This is another way to force logging out of cmdlets. it is a bit more work but it’s the only way for some cmdlets:

$OutputText = Set-Service -Name AdobeARMservice -Verbose -StartupType Disabled -ErrorVariable ErrVar -OutVariable OutVar -WarningVariable WarnVar -ErrorAction SilentlyContinue | Out-String
Write-Log "[$OutputText] [$ErrVar] [$OutVar] [$WarnVar]"
Remove-variable OutputText,ErrVar,OutVar,WarnVar -ErrorAction SilentlyContinue

The only thing you will want to watch-out for is the -ErrorAction switch. You might need to set it to STOP to make Try/Catch work.

CAVEAT: Not all cmdlets work with both methods.
Expand-Archive is one example. This will work fine:

Expand-Archive -Path "$dirFiles\Stuff.zip" -DestinationPath "$env:ProgramFiles\Stuff" -Force -Verbose *>&1 | Out-String | write-log

Using the cmdlet variables method however, will record [] [] [] [] regardless of success or failure:

$OutputText = Expand-Archive -Path "$dirFiles\Stuff.zip" -DestinationPath "$env:ProgramFiles\Stuff" -Force -Verbose -ErrorVariable ErrVar -OutVariable OutVar -WarningVariable WarnVar -ErrorAction SilentlyContinue | Out-String
Write-Log "[$OutputText] [$ErrVar] [$OutVar] [$WarnVar]"
Remove-Variable OutputText,ErrVar,OutVar,WarnVar -ErrorAction SilentlyContinue

Some examples

Since lots of this is trial and error, here are snippets form some of my packages.
I’ll try to add more when I can.

#Get directory listing into the PSADT log file
#Note: DIR is an alias to Get-ChildItem
#Out-string   removes characters and object stuff that may make Write-log blow up
#-ErrorAction SilentlyContinue   exists in-case the folder does not exist
Write-log "[$(Dir "C:\Program Files\Office" -ErrorAction SilentlyContinue | Out-String)]"

#Move files
Move-Item -Path $FilePath -Destination $DestPath -Verbose *>&1 | Out-String | write-log

#Create a Scheduled Task using Win10 built-in cmdlet and exported XML file
[string]$TaskXmlFileContent = Get-Content "$dirFiles\MyTask.xml"
$results = Register-ScheduledTask -TaskName 'MyTaskName' -TaskPath '\MyTaskPath\' -Xml $TaskXmlFileContent -ErrorAction Continue *>&1 | Out-String
Write-log "[$Results]" 

#Stop and disable a scheduled task. log what happens: good or bad. and not kill the script
Write-Log "Disabling [Adobe Acrobat Update Service]..."
$DisabledTasksText = Disable-ScheduledTask -TaskName 'Adobe Acrobat Update Task' -ErrorVariable ErrVar -OutVariable OutVar -WarningVariable WarnVar -ErrorAction SilentlyContinue | Out-String
Write-Log "[$DisabledTasksText] [$ErrVar] [$OutVar] [$WarnVar]"
Remove-variable ErrVar,OutVar,WarnVar -ErrorAction SilentlyContinue

#Delete Scheduled Task 
$results = Unregister-ScheduledTask -TaskName 'MyTaskName' -TaskPath '\MyTaskPath\' -Confirm:$false -ErrorAction Continue -Verbose *>&1 | Out-String
Write-log "[$Results]" 

$OutputText  = Stop-Service -Name AdobeARMservice -Verbose -Force -ErrorAction SilentlyContinue -ErrorVariable errvar -OutVariable OutVar -WarningVariable WarnVar | Out-String
Write-Log "  [$OutputText] [$errvar] [$OutVar] [$WarnVar]"
$OutputText = Set-Service -Name AdobeARMservice -Verbose -StartupType Disabled -ErrorAction SilentlyContinue -ErrorVariable errvar -OutVariable OutVar -WarningVariable WarnVar | Out-String
Write-Log "  [$OutputText] [$errvar] [$OutVar] [$WarnVar]"
Remove-variable OutputText,ErrVar,OutVar,WarnVar -ErrorAction SilentlyContinue
	

#Add a Windows Firewall rule
Write-log "Adding Windows Firewall rules to LOCAL Windows firewall to Block DymoConnect EXE to do Updates Check"
[String]$RuleName = "Block DymoConnect Updates Check"
New-NetFirewallRule -DisplayName $RuleName -Direction Inbound -Program "C:\Program Files (x86)\DYMO\DYMO Connect\DYMOConnect.exe" -RemoteAddress Internet -Action Block -Verbose *>&1 | Out-String | write-log
New-NetFirewallRule -DisplayName $RuleName -Direction Outbound -Program "C:\Program Files (x86)\DYMO\DYMO Connect\DYMOConnect.exe" -RemoteAddress Internet -Action Block -Verbose *>&1 | Out-String | write-log

#Detect if running on a Laptop using PSADT's Test-Battery. If so, Suspend BitLocker for ONE reboot
$batteryStatus = Test-Battery -PassThru
If ($batteryStatus.IsLaptop) {
	Write-log "Laptop detected. Suspending BitLocker for ONE reboot"
	Suspend-BitLocker -MountPoint "C:" -RebootCount 1 -Verbose | Out-String | write-log
	$OutputText = Suspend-BitLocker -MountPoint "C:" -RebootCount 1 -ErrorVariable ErrVar -OutVariable OutVar -WarningVariable WarnVar -ErrorAction SilentlyContinue | Out-String
	Write-Log "results of Suspend-BitLocker command: [$OutputText] [$ErrVar] [$OutVar] [$WarnVar]"
	Remove-variable OutputText,ErrVar,OutVar,WarnVar -ErrorAction SilentlyContinue
} Else {
	Write-log "Laptop NOT detected."
}

With Invoke-RestMethod you cannot use redirection of streams and the Cmdlet variables don’t work. So I came up with this ugly thing:

$MyUri = "https://MyREST.server.com"
$MyJsonHeaders = "<Json Stuff>"
$MyCreds = Get-Credential -Message "Enter your credentials to the REST API."	
Try {
	$Results = Invoke-RestMethod -uri $MyUri -Method Post -Headers $MyJsonHeaders -ContentType application/json -Credential $MyCreds
	#Try to log results (may not work)
	Write-Log "Lets try to log results of REST response"
	Write-Log $Results.success
	Write-Log $Results.data
	Write-Log $Results.numRows
	Write-Log $Results[0]
} Catch {
	[String]$ErrorMessage = @{'Message:' = "$($_.Exception.Message)";'ScriptStackTrace:'="$($_.ScriptStackTrace)";'InnerException:'="$($_.Exception.InnerException)"} `
    | Format-Table -AutoSize -Wrap -HideTableHeaders | Out-String
	Write-Log $ErrorMessage
}
7 Likes