Getting Started - Utilizing the Web: Part 3 (More Invoke-RestMethod)

Welcome to my Getting Started with Windows PowerShell series!

Continuing With Invoke-RestMethod

In part 2, we went over Invoke-RestMethod's basics, including:

In this post, I will be going over some more ways to use Invoke-RestMethod to authenticate to different APIs. I will also go over how to send information to the API, and work with the results we get back.

API Key In Header

Some APIs will require you to authenticate with a key in the header information. I happen to own a LIFX light, and their API uses that method of authentication.

If you own a LIFX light, and want to follow along, you can get a personal access token here: https://cloud.lifx.com/settings.

Information recap:

Now let's get to work! I have my key, which let's just say is: 'YouNeedYourOwnKey123321$'

Let's store that in a variable.

$apiKey          = 'YouNeedYourOwnKey123321$'

The next step here is to store that key in the header information (as a hashtable), with the key name of "Authorization".

$headers         =  @{Authorization=("Bearer {0}" -f $apiKey)}

Let's take a peek at $headers to ensure it contains what we need.

Looks good! Now to check it out. The URL in the documentation to get the lights available is 'https://api.lifx.com/v1/lights/all'. I like to store information like this in variables. 

$allURL          = 'https://api.lifx.com/v1/lights/all'

Now to put it all to use! We'll be utilizing Invoke-RestMethod with the following parameters/arguments:

  • Headers
    • Since we already have our headers stored in a nifty hashtable, we'll simply specify $headers
  • Uri
    • We'll pass along $allURL, and attempt to get a list of available lights

Here's all of the code so far. 

We'll be storing the results of the Invoke-RestMethod command in $ninjaLights.

$apiKey          = 'YouNeedYourOwnKey123321$'
$headers         =  @{Authorization=("Bearer {0}" -f $apiKey)}
$allURL          = 'https://api.lifx.com/v1/lights/all'

$ninjaLights     = Invoke-RestMethod -Headers $headers -Uri $allURL -ContentType 'application/json'

Success! 

Sending Data to a REST API via Body (LIFX API)

Now that we've authenticated to LIFX, we can work on sending information over, and control the light. Here are the steps I generally follow when using an API for the first time:

  • Review documentation
  • Review documentation (This can't be emphasized enough)
  • Test out a bunch of different ways to utilize the API
    • Get errors, handle errors, see what's expected, clean up the code, and build a function

Here is a snippet from their documentation:

What that tells us is how the text is to be formatted (JSON)and that we'll be using the PUT method.

Here are the parameters they accept:

Let's get started by ensuring we have working code, and then build a function around it.

The URL is 'https://api.lifx.com/v1/lights/all/state', which we'll store in $lightStateURL. We'll also setup some variables for the accepted parameters, as follows:

  • $state
    • on or off, we'll choose on
  • $color
    • We'll set this to red
  • $brightness
    • We'll set this to .5, or 50%
  • $duration
    • This will be set to .0, which is indefinitely

After setting up these variables, we'll use a here-string to build the payload, and finally use Invoke-RestMethod with the following parameters/arguments:

  • Uri
    • The $lightStateURL variable
  • Method
    • We'll be using PUT, per their documentation
  • Headers
  • Body
    • We will set this to $payloadBuilder (which is the here-string that contains the parameters we're passing to the LIFX API)

Here is the full code so far to test this:

$apiKey          = 'YouNeedYourOwnKey123321$'
$headers         =  @{Authorization=("Bearer {0}" -f $apiKey)}
$allURL          = 'https://api.lifx.com/v1/lights/all'

$lightStateURL = 'https://api.lifx.com/v1/lights/all/state'

$state      = 'on'
$color      = 'red'
$brightness = 0.5
$duration   = 0.0

$payload = [PSCustomObject] ${

    power      = $state,
    color      = $color,
    brightness = $brightness,
    duration   = $duration
}
$jsonPayload = $payload | ConvertTo-Json

$setResults = Invoke-RestMethod -Uri $lightStateURL -Method Put -Headers $headers -Body $jsonPayload -ContentType 'application/json'

Let's go ahead and run that to see if it worked:

This is an easy one to verify!

We can also take a look at the results property of $setResults.

Now that we know that it works, it can be used in a function to control the light.

Here is the code for an example of how to build a function with this information:

$apiKey          = 'YouNeedYourOwnKey123321'
$headers         =  @{Authorization=("Bearer {0}" -f $apiKey)}
$baseURL         = 'https://api.lifx.com/v1/lights'

function Set-LightState { #Begin function Set-LightState
    [cmdletbinding()]
    Param (
        [Parameter(Mandatory)]
        $headers,
        [String]
        $state = 'on',
        [String]
        $color = 'White',
        [Double]
        $brightness = 0.5,
        [Double]
        $duration = 0.0
    )
    
    #Get lights
    $ninjaLights = Invoke-RestMethod -Headers $headers -Uri $allURL

    #If our light shows as connected...
    if ($ninjaLights.Connected) {

        #Set the selected as the ID of the light (per LIFX documentation)
        $selector = $ninjaLights.id
        
        #Construct the URL to include the selector and /state
        $fullURL  = "$baseurl/$selector/state"        

        #Build the payload
        $payload = [PSCustomObject] @{

            power      = $state
            color      = $color
            brightness = $brightness
            duration   = $duration
        }

        #Convert payload to JSON
        $jsonPayload = $payload | ConvertTo-Json

        Write-Host "Changing light from:" 
        Write-Host `t"State     : $($ninjaLights.Power)" 
        Write-Host `t"Color     : $($ninjaLights.Color)"
        Write-Host `t"Brightness: $($($ninjaLights.Brightness * 100))%"`n

        Write-Host "Changing light to:" 
        Write-Host `t"State     : $state" 
        Write-Host `t"Color     : $color"
        Write-Host `t"Brightness: $($brightness * 100)%" 
        Write-Host `t"Duration  : $duration"`n

        #Store results in $setResults
        $setResults = Invoke-RestMethod -Uri $fullURL -Method Put -Headers $headers -Body $jsonPayload -ContentType 'application/json'
    
        Write-Host "API status:"
        Write-Host `t"Light :" $setResults.results.label
        Write-Host `t"Status:" $setResults.results.status `n
    
    }

} #End function Set-LightState

Set-LightState -headers $headers -color blue

Let's see what happens when we run it! 

(NOTE) I use Write-Host here merely as a tool to give you a visual of the data you are working with. Ultimately it would be best to create some custom objects, and return the results as you need to with the code you're writing.

The information here is provided to show you what you can do, how you can build a payload, and then work with the data you are returned. You can do anything you put your mind to, from silly things (the next example), to even using it with another script (that may get weather information), and then flash the light red a few times if there is a weather alert.

For this next example, I will utilize a loop, and our new function, to confuse my girlfriend. Well, with all the testing I've been doing... maybe not anymore ;) 

Here's the code:

[array]$colors   = @('white','red','orange','yellow','cyan','green','blue','purple','pink')

$originalBrightness = $ninjaLights.Brightness
$originalColor      = $ninjalights.Color
$originalState      = $ninjalights.Power
$colorString        = "hue:" + $originalcolor.hue + " saturation:" + $originalcolor.saturation + " kelvin:" + $originalColor.Kelvin
        
$i = 0
        
While ($i -le 20) {
            
    $color              = Get-Random $colors
    [double]$brightness = "{0:n2}" -f (Get-Random -Maximum 1 -Minimum 0.00)
 
    Set-LightState -headers $headers -color $color -brightness $brightness -state $state
    Start-Sleep -seconds 1

    $i++
            
}
        
Set-LightState -headers $headers -state $originalState -color $colorString -brightness $originalBrightness

And off we go!

It worked! 

At the end it will set the light back to the value it was discovered with:

If you'd like a deeper dive into this, check out my post on using the Lifx API with PowerShell, here: http://www.gngrninja.com/script-ninja/2016/2/6/powershell-control-your-lifx-light

Username/Password in Request URL (PRTG API)

Some APIs authenticate you via including a username/password (hopefully eventually password hash) in the request URL. PRTG has one of those APIs. PRTG is one of my favorite monitoring tools, as not only is it great out of the box, but it also has great synergy with PowerShell.

PRTG information:

Let's start by getting the credentials of the account you want to use with the API. This account will be an account that has access to do what you need to do in PRTG. I will use the PRTG admin account, but you'll want to ensure you use one setup just to use with the API.

$prtgCredential = Get-Credential

If you're demoing PRTG, and are using a self-signed cert, you'll need the following code to allow Invoke-RestMethod to work with the self-signed cert. This code will only affect your current session.

Add-Type @"
using System.Net;
using System.Security.Cryptography.X509Certificates;

    public class TemporarySelfSignedCert : ICertificatePolicy {
        public TemporarySelfSignedCert() {}
        public bool CheckValidationResult(
            ServicePoint sPoint, X509Certificate cert,
            WebRequest wRequest, int certProb) {
                return true;
            }
    }
"@

[System.Net.ServicePointManager]::CertificatePolicy = New-Object TemporarySelfSignedCert

Now let's setup a variable that contains our PRTG hostname, and a custom object with information for the API URLs.

$hostName = 'localhost'
$prtgInfo = [PSCustomObject]@{

    BaseURL        = "https://$hostName/api"
    SensorCountURL = "https://$hostName/api/table.xml?content=sensors&columns=sensor"
    
}

Next, per PRTG's documenation, we'll construct the URL we need to use to get the password hash.

$getHashURL = "$($prtgInfo.BaseURL)/getpasshash.htm?username=$($prtgCredential.userName)&password=$($prtgCredential.GetNetworkCredential().Password)"

Now we can finally use Invoke-RestMethod to get the hash:

$getHash    = Invoke-RestMethod -Uri $getHashURL

Let's make sure $getHash has our password hash.

Alright, got it! Now we can create a new PS Credential object to store our username, and password hash (instead of our password).

First. we'll convert the hash we created into a secure string. Then, we will create a new PS Credential object using the username we specified in $prtgCredential, and the secure string we just created as the password.

$passwordHash        = ConvertTo-SecureString  $getHash -AsPlainText -Force
$prtgCredentialwHash = New-Object System.Management.Automation.PSCredential ($($prtgCredential.UserName), $passwordHash)

We can then verify the information by displaying the contents of $prtgCredentialwHash, and also using the GetNetworkCredential().Password method.

Now that we know $prtgCredentialwHash contains what we need, we can construct a URL to test out the API.

Let's set some variables up:

$prtgUser         = $prtgCredentialwHash.UserName
$prtgPass         = $prtgCredentialwHash.GetNetworkCredential().Password

$credentialAppend = "&username=$prtgUser&passhash=$prtgPass"

We can check the value of $credentialAppend to ensure it is correct:

Now to construct the URL, and test out the API via Invoke-RestMethod.

Invoke-RestMethod -uri ($prtgInfo.SensorCountURL + $credentialAppend)

Note: The full URL that is constructed is (I cut out my actual password hash on purpose, you'll see your hash after the = if all went well):

If all went well, you will see the results of the request (if not, you'll see an unauthorized message).

Success!

Sending Data via URL (PRTG API)

The PRTG API accepts data in the URL of the request. The below example will pause a sensor for a specific duration, with a specific message:

#Set the $duration (in minutes)
$duration = 10
#Set $sensorID to the sensor we want to pause
$sensorID = 45
#Set the pause message
$message  = "Paused via PS PRTG"

#Construct the $pauseURL
$pauseURL     = "$($prtgInfo.baseURL)/pauseobjectfor.htm?id=$sensorID&pausemsg=$message&duration=$duration"

#Use Invoke-RestMethod with the $pauseURL + $credentialAppend
#We will always append $credentialAppend as this is how the API accepts authentication
Invoke-RestMethod -Uri ($pauseURL + $credentialAppend)

Awesome, it worked!

Mega Example With Concept Code

This last example contains some concept code for a project I'm working on. Feel free to judge and use it as you wish, however I will note now that it is nowhere near finalized. I'm still in the exploration, see what's possible, and try to get it all to work phase. 

Requirements for concept code to work:

  • PRTG Installaction
  • Folder structure
  • Module
    • NinjaLogging.psm1 (included in ZIP file below)

NOTE: I have not fully cleaned up or officially released any of the code yet. That includes the logging module. 

Here is my TODO list for the code:

  • Upload it to GitHub
  • Clean up/add error handling
  • Create readme files
  • Add parameter sets to some of the psprtg.ps1 functions
  • Fully convert psprtg.ps1 to a module
  • Add functionality to psprtg.ps1 to include an 'oh crap!' undo feature
    • This will include exporting a custom object with the device ID and action taken

With that said, here is a link to download it (or you can skip to the code if that's all you want to see):

psPRTG download

How to get it working/examples:

  • Import the script as a module via Import-Module .\psprtg.ps1
    • The first time you do this, it will ask for your PRTG credentials. The module will then export the credential object to the input folder, and then use that exported credential the next time the module is used
  • Contents of machine/user encrypted credential file stored in the input directory:

 

  • Find a device and pause it via Invoke-SensorModification -findDevice
    • Note: You can also specify -Duration and -Message
  • Use a text file list of devices to find and take an action on

Pausing example:

Resuming example:

Log File Contents:

Code

psprtg.ps1

#Setup
$scriptPath      = Split-Path -Parent -Path $MyInvocation.MyCommand.Definition
$moduleDir       = "$scriptPath\modules"
$logDir          = "$scriptPath\logs"
$outputDir       = "$scriptPath\output"
$inputDir        = "$scriptPath\input"
$hashFile        = "$inputDir\prtgCredentialwHash.xml"
$hostName        = 'localhost'
$modules         = ('ninjaLogging')

$prtgInfo = [PSCustomObject]@{

    BaseURL        = "https://$hostName/api"
    SensorCountURL = "https://$hostName/api/table.xml?content=sensors&columns=sensor"
    
}

function Invoke-ModuleImport { #Begin function Invoke-ModuleImport
    [cmdletbinding()]
    param($modules)

    Push-Location

    ForEach ($module in $modules) {

        if (Get-Module -ListAvailable -Name $module) {
    
            Import-Module $module
    
        } elseif (!(Get-Module -Name $module)) {
              
            Write-Verbose "Importing module: [$module] from: [$moduleDir\$module]" 
            Import-Module "$moduleDir\$module"

        } Else {

            Write-Verbose "Module [$module] already loaded!"

        }

    }

    Pop-Location

} #End function Invoke-ModuleImport

function Invoke-CredentialCheck { #Begin function Invoke-CredentialCheck
    [cmdletbinding()]
    Param()

    Try { #Begin try for credential path existence

        #Check if our hashed credential object exists
        if (Test-Path $hashFile) { #Begin if for testing credential path existence 

            #Import credentials from hash file

            Write-LogFile -logPath $logFile -logValue "Importing credentials from [$hashFile]" -Verbose

            $prtgCredentialwHash = Import-Clixml $hashFile

            #Add to info hash

            $prtgInfo | Add-Member -Type NoteProperty -Name Credentials -Value $prtgCredentialwHash

            #create credential string for URLs
            $prtgUser         = $prtgCredentialwHash.UserName
            $prtgPass         = $prtgCredentialwHash.GetNetworkCredential().Password

            $credentialAppend = "&username=$prtgUser&passhash=$prtgPass"
            
            Return $credentialAppend

        #If it doesn't exist, attempt to create it
        } else {

            Write-Host "We need to get your hashed password to use with the API!" `n
            Write-Host "Please enter your PRTG credentials"`n

            #Store credential in $prtgCredential
            $prtgCredential = Get-Credential

            #Get the password hash via ConvertTo-SecureString and the Get-PRTGPasswordHash function.
            #Note: If you are not using a self-signed certificate (hopefully not, but if it is a demo/test install you may be): use -selfSignedCert:$True
            $passwordHash   = ConvertTo-SecureString  "$(Get-PRTGPasswordHash -PRTGCredential $prtgCredential -selfSignedCert:$True)" -AsPlainText -Force

            #Create new credential object and export it to the input folder
            #This allows us to use it when the script runs
            $prtgCredentialwHash = New-Object System.Management.Automation.PSCredential ($($prtgCredential.UserName), $passwordHash)

            Write-LogFile -logPath $logFile -logValue "Exporting hashed credentials to [$hashFile]." -Verbose

            #Export to file
            $prtgCredentialwHash | Export-Clixml $hashFile

            #create credential string for URLs
            $prtgUser         = $prtgCredentialwHash.UserName
            $prtgPass         = $prtgCredentialwHash.GetNetworkCredential().Password

            $credentialAppend = "&username=$prtgUser&passhash=$prtgPass"

            Return $credentialAppend

        }  #End if for testing credential path existence

    } #End try for credential path existence

    Catch {

        $errorMessage = $_.Exception.Message

        Write-LogFileError -logPath $logFile -errorDesc "Error while checking for credentials/importing credentials: [$errorMessage]!"
    
        #Resolve the log file
        Resolve-LogFile -logPath $logFile

        Break

    }

} #End function Invoke-CredentialCheck

function Get-PRTGPasswordHash { #Begin function Get-PRTGPasswordHash
    [cmdletbinding()]
    param(
        [Parameter(Mandatory)]
        [PSCredential]
        $PRTGCredential,

        [Parameter()]
        [Boolean]
        $selfSignedCert = $false
    )

    if ($selfSignedCert) {

        Try {

            Add-Type @"
                using System.Net;
                using System.Security.Cryptography.X509Certificates;

                    public class TemporarySelfSignedCert : ICertificatePolicy {
                    public TemporarySelfSignedCert() {}
                    public bool CheckValidationResult(
                        ServicePoint sPoint, X509Certificate cert,
                        WebRequest wRequest, int certProb) {
                        return true;
                    }
                }
"@

            [System.Net.ServicePointManager]::CertificatePolicy = New-Object TemporarySelfSignedCert

    }

        Catch {
        
            $errorMessage = $_.Exception.Message

            Write-LogFileError -logPath $logFile -errorDesc "Error while allowing self signed certs: [$errorMessage]" -ForegroundColor Red -BackgroundColor DarkBlue
            
            #Resolve the log file
            Resolve-LogFile -logPath $logFile

            Break


        }

    }

    Try {

        $getHashURL = "$($prtgInfo.BaseURL)/getpasshash.htm?username=$($prtgCredential.userName)&password=$($prtgCredential.GetNetworkCredential().Password)"
        $getHash    = Invoke-RestMethod -Uri $getHashURL 

        Return $getHash

    }

    Catch {

        $errorMessage = $_.Exception.Message

        Write-LogFileError -logPath $logFile -errorDesc "Error while getting hash: [$errorMessage]" -Verbose

        Break

    }

} #End function Get-PRTGPasswordHash

function Invoke-DeviceSearch { #Begin function Invoke-DeviceSearch
    [cmdletbinding()]
    Param(
        [Parameter(Mandatory)]
        $findMe
    )

    [System.Collections.ArrayList]$foundDeviceIDs = @()

    ForEach ($find in $findMe) {

        Try {

            $getDeviceURL       = "$($prtgInfo.BaseURL)/table.json?content=devices&output=json&columns=objid,probe,group,device,host,downsens,partialdownsens,downacksens,upsens,warnsens,pausedsens,unusualsens,undefinedsens"
            $devices            = Invoke-RestMethod -Uri ($getDeviceURL + $credentialAppend) 
            $findDeviceWildCard = '*' + $find + '*'

            ForEach ($device in $devices.devices) {

                Switch ($device.device) {

                    {$_ -like $findDeviceWildCard} {
                    
                        $foundDeviceIDs.Add($device.objid) | Out-Null
                    
                    }

                }

            } 
        
        }

        Catch {

            $errorMessage = $_.Exception.Message

            Write-LogFileError -logPath $logFile -errorDesc "Unable to get device list: [$errorMessage]" -Verbose

        }

    }

    Return $foundDeviceIDs

} #End function Invoke-DeviceSearch

function Invoke-SensorModification { #Begin function Invoke-SensorModification
    [cmdletbinding()]
    Param(
        [parameter(Mandatory)]
        [string]
        $action,
        [parameter()]
        [string]
        $findDevice,
        [Parameter()]
        [int]
        $sensorID,
        [Parameter()]
        [String]
        $message,
        [Parameter()]
        [int]
        $duration,
        [Parameter()]
        $List
    )

    $logFile = New-logFile -logPath $logDir -logName 'PSPRTG.log' -addDate:$true

    if (!$duration) {

        $duration = 30

    }

    if (!$message) {

        $message = "Sensor paused by: [$((Get-ChildItem Env:\USERNAME).Value)] via PS PRTG."    

    }

    if ($List) {
        
        Switch ($List | Get-Member | Select-Object -ExpandProperty TypeName -Unique) {

            {$_ -eq 'System.String'} {

                if ($list -like '*.txt') {
                
                    Write-Host "File detected" 

                    $deviceList = Get-Content $list

                    $foundDeviceIDs = Invoke-DeviceSearch -findMe $deviceList

                } else {

                    Write-Host 'Single string or array detected'

                    $deviceList = $list 

                    $foundDeviceIDs = Invoke-DeviceSearch -findMe $deviceList

                }

            }

            {$_ -eq 'System.Management.Automation.PSCustomObject'} {

                Write-Host 'Custom Object detected'

            }

        }

    }

    if ($findDevice) {

        Try {
            
            $foundDeviceIDs = Invoke-DeviceSearch -findMe $findDevice
        
        }

        Catch {

            $errorMessage = $_.Exception.Message

            Write-LogFileError -logPath $logFile -errorDesc "Unable to get device list: [$errorMessage]" -Verbose
            
            #Resolve the log file
            Resolve-LogFile -logPath $logFile

            Break
        
        }

    }

    Switch ($action) { #Begin switch for PRTG action

        {$_ -eq 'Pause'} {

            if ($findDevice -or $List) {

                ForEach ($id in $foundDeviceIDs) {

                    $sensorID     = $null
                    $sensorID     = $id

                    $pauseURL     = "$($prtgInfo.baseURL)/pauseobjectfor.htm?id=$sensorID&pausemsg=$message&duration=$duration"
        
                    Write-LogFile -logPath $logFile -Value "Attempting to pause sensor ID: [$sensorID], for duration of [$duration], with message [$message]" -Verbose
            
                    $pauseAttempt = Invoke-WebRequest -Uri ($pauseURL + $credentialAppend)   
                
                } 

                #Resolve the log file
                Resolve-LogFile -logPath $logFile 

            } 
            
            Else { 

                $pauseURL     = "$($prtgInfo.baseURL)/pauseobjectfor.htm?id=$sensorID&pausemsg=$message&duration=$duration"
        
                Write-LogFile -logPath $logFile -Value "Attempting to pause sensor ID: [$sensorID], for duration of [$duration], with message [$message]" -Verbose
            
                $pauseAttempt = Invoke-WebRequest -Uri ($pauseURL + $credentialAppend)

                #Resolve the log file
                Resolve-LogFile -logPath $logFile 
            

            }

        }

        {$_ -eq 'Resume'} {

            if ($findDevice -or $List) {

                ForEach ($id in $foundDeviceIDs) {

                    $sensorID     = $null
                    $sensorID     = $id

                    $resumeURL     = "$($prtgInfo.baseURL)/pause.htm?id=$sensorID&action=1"
        
                    Write-LogFile -logPath $logFile -Value "Attempting to resume sensor ID: [$sensorID]." -Verbose
            
                    $resumeAttempt =  Invoke-WebRequest -Uri ($resumeURL + $credentialAppend)
                
                }
                
                #Resolve the log file
                Resolve-LogFile -logPath $logFile 

            } 
            
            Else { 

                $resumeURL     = "$($prtgInfo.baseURL)/pause.htm?id=$sensorID&action=1"
        
                Write-LogFile -logPath $logFile -Value "Attempting to resume sensor ID: [$sensorID]." -Verbose
            
                $resumeAttempt =  Invoke-WebRequest -Uri ($resumeURL + $credentialAppend)
            
                #Resolve the log file
                Resolve-LogFile -logPath $logFile 

            }

        }

        Default {

            Write-LogFileError -logPath $logFile -errorDesc "Unable to perform action [$action], as it does not match any valid actions!" -Verbose

            Resolve-LogFile -logPath $logFile 

            Break

        }

    } #End switch for PRTG action

} #End function Invoke-SensorModification

#Test API
Try{

    #Import modules 
    ForEach ($module in $modules) {Invoke-ModuleImport -modules $module}

    #Create log file
    $logFile = New-logFile -logPath $logDir -logName 'PSPRTG.log' -addDate:$true

    #Attempt to import/set credentials and get the returned string to append to the request for authentication
    $credentialAppend = Invoke-CredentialCheck
    
    #Attempt to get information as a test to see if the API will work
    Invoke-RestMethod -uri ($prtgInfo.SensorCountURL + $credentialAppend) | Out-Null

    #If it works, set TestPassed as true
    $prtgInfo | Add-Member -MemberType NoteProperty -Name TestPassed -Value $true
    
}

Catch {

    $errorMessage = $_.Exception.Message
    
    Write-LogFileError -logPath $logFile -errorDesc "Error while attempting to use API: [$errorMessage]" -Verbose

    #If it doesn't work, set TestPassed as false and break out
    $prtgInfo | Add-Member -MemberType NoteProperty -Name TestPassed -Value $false

    #Resolve the log file
    Resolve-LogFile -logPath $logFile

    Break

}

NinjaLogging.psm1

Set-StrictMode -Version Latest

$scriptPath = Split-Path -Parent -Path $MyInvocation.MyCommand.Definition

Switch ($MyInvocation.PSCommandPath) {

    {$_ -match '\\\\'} {
    
    
        $scriptName = $_.SubString($_.LastIndexOf('\')+1)
    
    }

    Default { 
    
        $scriptName = Get-ChildItem $MyInvocation.PSCommandPath | Select-Object -ExpandProperty BaseName
        
    }

}

if ('Count' -in ($scriptName.psobject.Members.Name)) {
    
    $scriptName = 'ScriptLog'
    
}

function New-LogFile {
<#
.SYNOPSIS
   New-LogFile will create a log file.

.DESCRIPTION
   New-LogFile will create a log file. 

   You can specify different paramaters to change the file's name, and where it is stored.
   By default it will attempt to get the name of the calling function or script via $scriptName = (Get-ChildItem $MyInvocation.PSCommandPath | Select-Object -ExpandProperty BaseName).
   It will also attempt to get the path via $scriptPath = Split-Path -Parent -Path $MyInvocation.MyCommand.Definition.
   You can also specify the path and name, as well as if you'd like to append the date in the following format: MM-dd-yy_HHmm.

   Use the -Verbose parameter to display what is happening to the host.

.PARAMETER logPath
    Alias: Path
    Type : String

    Specify the path to the logfile

.PARAMETER logName    
    Alias: Name
    Type : String

    Specify the name of the log file. Be sure to include the extension if specifying the name.

.PARAMETER scriptVersion
    Type : Double

    Specify the version of your script being run. If left blank, will default to 0.1

.PARAMETER addDate
    Type : Boolean

    Specify if you'd like to add the date to the file name.  If you're specifying logName, you can use addDate to append the current date/time in the format: MM-dd-yy_HHmm.

.NOTES
    Name: New-LogFile
    Version: 1.0
    Author: Ginger Ninja (Mike Roberts)
    DateCreated: 5/11/16
    

.LINK
    http://www.gngrninja.com

.EXAMPLE
    $logFile = New-LogFile 
    -----------------------------
    
    gngrNinja> $logFile
    C:\PowerShell\logs\ScriptLog_05-11-16_1612.log

.EXAMPLE
    $logFile = New-LogFile -Verbose
    -----------------------------
    
    VERBOSE: No path specified. Using: C:\PowerShell\logs
    VERBOSE:
    VERBOSE: No log name specified. Setting log name to: ScriptLog.log and adding date.
    VERBOSE:
    VERBOSE: Adding date to log file with an extension! New file name:
    ScriptLog_05-11-16_1613.log
    VERBOSE:
    VERBOSE: Created C:\PowerShell\logs\ScriptLog_05-11-16_1613.log
    VERBOSE:
    VERBOSE: File C:\PowerShell\logs\ScriptLog_05-11-16_1613.log created and verified to
    exist.
    VERBOSE:
    VERBOSE: Adding the following information to:
    C:\PowerShell\logs\ScriptLog_05-11-16_1613.log
    VERBOSE:
    VERBOSE: -----------------------------------------------------------------
    VERBOSE: Started logging at [05/11/2016 16:13:11]
    VERBOSE: Script (Version 0.1) executed by: [thegn] on computer: [GINJA10]
    VERBOSE: -----------------------------------------------------------------
    VERBOSE:
    gngrNinja>

.EXAMPLE
    $logfile = New-LogFile -Name 'testName.log' -path  'c:\temp' -addDate $true -Verbose
    -----------------------------

    VERBOSE: Adding date to log file with an extension! New file name:
    testName_05-11-16_1615.log
    VERBOSE:
    VERBOSE: Created c:\temp\testName_05-11-16_1615.log
    VERBOSE:
    VERBOSE: File C:\temp\testName_05-11-16_1615.log created and verified to exist.
    VERBOSE:
    VERBOSE: Adding the following information to: C:\temp\testName_05-11-16_1615.log
    VERBOSE:
    VERBOSE: -----------------------------------------------------------------
    VERBOSE: Started logging at [05/11/2016 16:15:13]
    VERBOSE: Script (Version 0.1) executed by: [thegn] on computer: [GINJA10]
    VERBOSE: -----------------------------------------------------------------
    VERBOSE:

.OUTPUTS
    Full path to the log file created.
#>
    [cmdletbinding()]
    param(
        [Parameter(Mandatory = $false,
                   Position  = 0)]
        [Alias('Path')]
        [string]
        $logPath,
        [Parameter(Mandatory = $false,
                   Position  = 1)]
        [Alias('Name')]
        [string]
        $logName,
        [Parameter(Mandatory = $false,
                   Position  = 2)]
        [double]
        $scriptVersion = 0.1,
        [Parameter(Mandatory = $false,
                   Position  = 3)]
        [boolean]
        $addDate = $false
    )

#Check if file/path are set
    if (!$logPath) {
        
        $logPath = "$scriptPath\logs"
        
        Write-Verbose "No path specified. Using: $logPath"
        Write-Verbose ""
        
    }
    
    if (!$logName) {
        
        $logName = $scriptName + '.log'
        $addDate = $true
        
        Write-Verbose "No log name specified. Setting log name to: $logName and adding date."
        Write-Verbose ""
      
    }

    #Check if $addDate is $true, take action if so
    if ($addDate) {
        
        if ($logName.Contains('.')) {
            
            $logName = $logName.SubString(0,$logName.LastIndexOf('.')) + "_{0:MM-dd-yy_HHmm}" -f (Get-Date) + $logName.Substring($logName.LastIndexOf('.'))
            
            Write-Verbose "Adding date to log file with an extension! New file name: $logName"
            Write-Verbose ""
           
        } else {
            
            $logName = $logName + "_{0:MM-dd-yy_HHmm}" -f (Get-Date)
            
            Write-Verbose "Adding date to log file. New file name: $logName"
            Write-Verbose ""
            
        }
         
    }
    
    #Variable set up
    $time     = Get-Date
    $fullPath = $logPath + '\' + $logName
    $curUser  = (Get-ChildItem Env:\USERNAME).Value
    $curComp  = (Get-ChildItem Env:\COMPUTERNAME).Value
    
    #Checking paths / Creating directory if needed
    
    if (!(Test-Path $logPath)) {
        
        Try {
            
            New-Item -Path $logPath -ItemType Directory -ErrorAction Stop | Out-Null
            
            Write-Verbose "Folder $logPath created as it did not exist."
            Write-Verbose ""
            
        }
        
        Catch {
            
            $message = $_.Exception.Message
            
            Write-Output "Could not create folder due to an error. Aborting. (See error details below)"
            Write-Error $message
            
            Break
          
        }
    
    }
    
    #Checking to see if a file with the name name exists, renaming it if so.
    if (Test-Path $fullPath) {
        
        Try {
            
            $renFileName = ($fullPath + (Get-Random -Minimum ($time.Second) -Maximum 999) + 'old')
            
            Rename-Item $fullPath -NewName ($renFileName.Substring($renFileName.LastIndexOf('\')+1)) -Force -ErrorAction Stop | Out-Null
            
            Write-Verbose "Renamed $fullPath to $($renFileName.Substring($renFileName.LastIndexOf('\')+1))"
            Write-Verbose ""
            
        }
        
        Catch {
            
            $message = $_.Excetion.Message
            
            Write-Output "Could not rename existing file due to an error. Aborting. (See error details below)"
            Write-Error $message
            
            Break
            
        }
        
    }
    
    #File creation
    Try {
        
        New-Item -Path $fullPath -ItemType File -ErrorAction Stop | Out-Null
        
        Write-Verbose "Created $fullPath"
        Write-Verbose ""
        
    } 
    
    Catch {
        
        $message = $_.Exception.Message
        
        Write-Output "Could not create directory due to an error. Aborting. (See error details below)"
        Write-Error $message
        
        Break
        
    }
    
    #Get the full path in case of dot sourcing
    $fullPath = (Get-ChildItem $fullPath).FullName
    
    if (Test-Path $fullPath) {
        
        $flairLength = ("Script (Version $scriptVersion) executed by: [$curUser] on computer: [$curComp]").Length + 1
        
        Write-Verbose "File $fullPath created and verified to exist."
        Write-Verbose ""
        Write-Verbose "Adding the following information to: $fullPath"
        Write-Verbose ""
        Write-Verbose ('-'*$flairLength)
        Write-Verbose "Started logging at [$time]"
        Write-Verbose "Script (Version $scriptVersion) executed by: [$curUser] on computer: [$curComp]"
        Write-Verbose ('-'*$flairLength)
        Write-Verbose ""
        
        Add-Content -Path $fullPath -Value ('-'*$flairLength)
        Add-Content -Path $fullPath -Value "Started logging at [$time]"
        Add-Content -Path $fullPath -Value "Script (Version $scriptVersion) executed by: [$curUser] on computer: [$curComp]"
        Add-Content -Path $fullPath -Value ('-'*$flairLength)
        Add-Content -Path $fullPath -Value ""
        
        Return [string]$fullPath
         
    } else {
        
        Write-Error "File $fullPath does not exist. Aborting script."

        Break
        
    }
       
}

function Write-LogFile {
<#
.SYNOPSIS
   Write-LogFile will add information to a log file created with New-LogFile.

.DESCRIPTION
   Write-LogFile will add information to a log file created with New-LogFile.

   By default additions to the log file will include a timestamp, unless you specify -addTimeStamp $false.
   This function accepts values from the pipeline, as demonstrated in an example.

   Use the -Verbose parameter to display what is being logged to the host.
   
.PARAMETER logPath
    Alias: Path
    Type : String

    Specify the full path to the log file, including the name.

.PARAMETER logValue
    Alias: Value
    Type : String

    Specify the value(s) you'd like logged.

.PARAMETER addTimeStamp
    Type : Boolean

    Defaults to true, set to false if you'd like to omit the timestamp.

.NOTES
    Name: Write-LogFile
    Version: 1.0
    Author: Ginger Ninja (Mike Roberts)
    DateCreated: 5/11/16

.LINK
    http://www.gngrninja.com

.EXAMPLE
    For this example we'll assume you use:
    $logFile = New-LogFile 

    Write-LogFile -logPath $logFile -logValue 'test log value!'
    -----------------------------

    gngrNinja> more $logfile
    -----------------------------------------------------------------
    Started logging at [05/11/2016 16:19:37]
    Script (Version 0.1) executed by: [thegn] on computer: [GINJA10]
    -----------------------------------------------------------------

    [05-11-16 16:23:24] test log value!

.EXAMPLE
    For this example we'll assume you use:
    $logFile = New-LogFile 

    Write-LogFile -logPath $logFile -logValue 'test log value!' -Verbose
    -----------------------------
    
    VERBOSE: Adding [05-11-16 16:25:19] test log value! to
    C:\PowerShell\logs\ScriptLog_05-11-16_1619.log
    VERBOSE:

.EXAMPLE
    For this example we'll assume you use:
    $logFile = New-LogFile 

    Get-Process | Write-LogFile $logFile -Verbose
    -----------------------------

    ...
    VERBOSE: Adding [05-11-16 16:26:51] System.Diagnostics.Process (wininit) to
    C:\PowerShell\logs\ScriptLog_05-11-16_1619.log
    VERBOSE:
    VERBOSE: Adding [05-11-16 16:26:51] System.Diagnostics.Process (winlogon) to
    C:\PowerShell\logs\ScriptLog_05-11-16_1619.log
    VERBOSE:
    VERBOSE: Adding [05-11-16 16:26:51] System.Diagnostics.Process (WmiPrvSE) to
    C:\PowerShell\logs\ScriptLog_05-11-16_1619.log
    VERBOSE:
    VERBOSE: Adding [05-11-16 16:26:51] System.Diagnostics.Process (WmiPrvSE) to
    C:\PowerShell\logs\ScriptLog_05-11-16_1619.log
    VERBOSE:
    VERBOSE: Adding [05-11-16 16:26:51] System.Diagnostics.Process (WUDFHost) to
    C:\PowerShell\logs\ScriptLog_05-11-16_1619.log
    VERBOSE:
    ...

.EXAMPLE
    For this example we'll assume you use:
    $logFile = New-LogFile 

    Write-LogFile -logPath $logFile -logValue 'test without timestamp' -addTimeStamp $false -Verbose
    -----------------------------

    VERBOSE: Adding test without timestamp to C:\PowerShell\logs\ScriptLog_05-11-16_1631.log
    VERBOSE:
#>
    [cmdletbinding()]
    param(
        [Parameter(Mandatory = $true,
                   Position  = 0)]
        [Alias('Path')]
        [string]
        $logPath,
        [Parameter(Mandatory                       = $true,
                   ValueFromPipeline               = $true,
                   ValueFromPipelineByPropertyName = $true,
                   Position                        = 1)]
        [Alias('Value')]
        [string]
        $logValue,
        [Parameter(Mandatory = $false,
                   Position  = 2)]
        [boolean]
        $addTimeStamp = $true
    )
    
    Begin {

        if (!(Test-Path $logPath)) {
        
            Write-Error "Unable to access $logPath"

            Break
        
        } 

    }

    Process {

        ForEach ($value in $logValue) {
        
            $timeStamp = "[{0,0:MM}-{0,0:dd}-{0,0:yy} {0,0:HH}:{0,0:mm}:{0,0:ss}]" -f (Get-Date)

            if ($addTimeStamp) {
            
                $value = "$($timeStamp + ' ' + $value)"
           
            }
        
            Write-Verbose "Adding $value to $logPath"
            Write-Verbose ""
        
            Add-Content -Path $logPath -Value $value
            Add-Content -Path $logPath -Value ''

        }
         
    }

}

function Write-LogFileError {
<#
.SYNOPSIS
   Write-LogFileError will add information to a log file created with New-LogFile. The information will be prepended with [ERROR].

.DESCRIPTION
   Write-LogFileError will add information to a log file created with New-LogFile. The information will be prepended with [ERROR].

   By default additions to the log file will include a timestamp, unless you specify -addTimeStamp $false.
   This function accepts values from the pipeline, as demonstrated in an example.

   Use the -Verbose parameter to display what is being logged to the host.
   
.PARAMETER logPath
    Alias: Path
    Type : String

    Specify the full path to the log file, including the name.

.PARAMETER errorDesc
    Alias: Value
    Type : String

    Specify the value(s) you'd like logged as errors.

.PARAMETER addTimeStamp
    Type : Boolean

    Defaults to true, set to false if you'd like to omit the timestamp.

.PARAMETER exitScript
    Alias: Exit
    Type : Boolean

    This parameter let's you specify $true if you'd like to exit the script after the error is logged. 
    It defaults to $false.
    
.NOTES
    Name: Write-LogFileError
    Version: 1.0
    Author: Ginger Ninja (Mike Roberts)
    DateCreated: 5/11/16

.LINK
    http://www.gngrninja.com

.EXAMPLE
    For this example we'll assume you use:
    $logFile = New-LogFile 

    Write-LogFileError -logPath $logFile -errorDesc 'test log value error!'
    -----------------------------

    gngrNinja> more $logFile
    -----------------------------------------------------------------
    Started logging at [05/11/2016 16:31:44]
    Script (Version 0.1) executed by: [thegn] on computer: [GINJA10]
    -----------------------------------------------------------------

    [05-11-16 16:31:51] [ERROR ENCOUNTERED]: test log value error!
#>
    [CmdletBinding()]
        param(
            [Parameter(Mandatory = $true,
                       Position  = 0)]
            [Alias('Path')]
            [string]
            $logPath,
            [Parameter(Mandatory                       = $true,
                       ValueFromPipeline               = $true,
                       ValueFromPipelineByPropertyName = $true,
                       Position                        = 1)]
            [string]
            $errorDesc,
            [Parameter(Mandatory = $false,
                       Position  = 2)]
            [boolean]
            $addTimeStamp = $true,
            [Parameter(Mandatory = $false,
                       Position  = 3)]
            [Alias('Exit')]           
            [boolean]
            $exitScript = $false
        )
    
    Begin {

        if (!(Test-Path $logPath)) {
    
            Write-Error "Unable to access $logPath"
            Break

        }

    }

    Process {
     
        ForEach ($value in $errorDesc) { 

            $timeStamp = "[{0,0:MM}-{0,0:dd}-{0,0:yy} {0,0:HH}:{0,0:mm}:{0,0:ss}]" -f (Get-Date)

            $value     = "[ERROR ENCOUNTERED]: $value"
        
            if ($addTimeStamp) {
            
                $value = "$($timeStamp + ' ' + $value)"
           
            }
        
            Write-Verbose "Adding $value to $logPath"
            Write-Verbose ""
        
            Add-Content -Path $logPath -Value $value
            Add-Content -Path $logPath -Value ''

        }

    }

    End {
         
         if ($exitScript) {
            
            Write-Verbose "Performing log file close command: Resolve-LogFile -logPath $logPath -exitonCompletion $true"
            Write-Verbose ""
            
            Resolve-LogFile -logPath $logPath -exitScript $true
            
        }

    }
 
    
    
}

function Write-LogFileWarning {
<#
.SYNOPSIS
   Write-LogFileWarning will add information to a log file created with New-LogFile. The information will be prepended with [ERROR].

.DESCRIPTION
   Write-LogFileWarning will add information to a log file created with New-LogFile. The information will be prepended with [ERROR].

   By default additions to the log file will include a timestamp, unless you specify -addTimeStamp $false.
   This function accepts values from the pipeline, as demonstrated in an example.

   Use the -Verbose parameter to display what is being logged to the host.
   
.PARAMETER logPath
    Alias: Path
    Type : String

    Specify the full path to the log file, including the name.

.PARAMETER warningDesc
    Alias: Value
    Type : String

    Specify the value(s) you'd like logged as errors.

.PARAMETER addTimeStamp
    Type : Boolean

    Defaults to true, set to false if you'd like to omit the timestamp.

.PARAMETER exitScript
    Alias: Exit
    Type : Boolean

    This parameter let's you specify $true if you'd like to exit the script after the error is logged. 
    It defaults to $false.

.NOTES
    Name: Write-LogFileWarning
    Version: 1.0
    Author: Ginger Ninja (Mike Roberts)
    DateCreated: 5/11/16

.LINK
    http://www.gngrninja.com

.EXAMPLE
    For this example we'll assume you use:
    $logFile = New-LogFile 

    Write-LogFileWarning -logPath $logFile -warningDesc 'test log value warning!'
    -----------------------------

    gngrNinja> more $logFile
    -----------------------------------------------------------------
    Started logging at [05/11/2016 16:38:29]
    Script (Version 0.1) executed by: [thegn] on computer: [GINJA10]
    -----------------------------------------------------------------

    [05-11-16 16:38:48] [WARNING]: test log value warning!
#>
    [CmdletBinding()]
        param(
            [Parameter(Mandatory = $true,
                       Position  = 0)]
            [Alias('Path')]
            [string]
            $logPath,
            [Parameter(Mandatory                       = $true,
                       ValueFromPipeline               = $true,
                       ValueFromPipelineByPropertyName = $true,
                       Position                        = 1)]
            [string]
            $warningDesc,
            [Parameter(Mandatory = $false,
                       Position  = 2)]
            [boolean]
            $addTimeStamp = $true,
            [Parameter(Mandatory = $false,
                       Position  = 3)]
            [Alias('Exit')]           
            [boolean]
            $exitScript = $false
        )
    
    Begin {

        if (!(Test-Path $logPath)) {
    
            Write-Error "Unable to access $logPath"

            Break

        }

    }

    Process {
     
        ForEach ($value in $warningDesc) { 

            $timeStamp = "[{0,0:MM}-{0,0:dd}-{0,0:yy} {0,0:HH}:{0,0:mm}:{0,0:ss}]" -f (Get-Date)

            $value     = "[WARNING]: $value"
        
            if ($addTimeStamp) {
            
                $value = "$($timeStamp + ' ' + $value)"
           
            }
        
            Write-Verbose "Adding $value to $logPath"
            Write-Verbose ""
        
            Add-Content -Path $logPath -Value $value
            Add-Content -Path $logPath -Value ''

        }

    }

    End {
         
         if ($exitScript) {
            
            Write-Verbose "Performing log file close command: Resolve-LogFile -logPath $logPath -exitonCompletion $true"
            Write-Verbose ""
            
            Resolve-LogFile -logPath $logPath -exitScript $true
            
        }

    }
    
}

function Resolve-LogFile {
<#
.SYNOPSIS
   Resolve-LogFile will resolve a created log file.

.DESCRIPTION
   Resolve-LogFile will resolve a created log file.

   Use the -Verbose parameter to display what is happening to the host.

.PARAMETER logPath
    Alias: Path
    Type : String

    Specify the full path, including name, to the log file to be resolved.

.PARAMETER logName    
    Alias: Name
    Type : String

    Specify the name of the log file. Be sure to include the extension if specifying the name.

.PARAMETER exitScript
    Alias: Exit
    Type : Boolean

    Specify $true if you'd like to exit the script after the log file is resolved. 
    It defaults to $false.

.NOTES
    Name: Resolve-LogFile
    Version: 1.0
    Author: Ginger Ninja (Mike Roberts)
    DateCreated: 5/11/16
    
.LINK
    http://www.gngrninja.com

.EXAMPLE
    $logFile = New-LogFile 
    -----------------------------
    
    gngrNinja> $logFile
    C:\PowerShell\logs\ScriptLog_05-11-16_1612.log

.EXAMPLE

    $logFile = New-LogFile 
    Get-Process | Write-LogFile $logFile

    Resolve-LogFile $logFile
    -----------------------------
    
    ...
    [05-11-16 16:43:58] System.Diagnostics.Process (wininit)

    [05-11-16 16:43:58] System.Diagnostics.Process (winlogon)

    [05-11-16 16:43:58] System.Diagnostics.Process (WmiPrvSE)

    [05-11-16 16:43:58] System.Diagnostics.Process (WmiPrvSE)

    [05-11-16 16:43:58] System.Diagnostics.Process (WUDFHost)

    ---------------------------------------------
    Ended logging at [05/11/2016 16:44:01]
    ---------------------------------------------
#>
    [cmdletbinding()]
    param(
        [parameter(Mandatory = $true,
                   Position  = 0)]
        [Alias('Path')]
        [string]
        $logPath,
        [Parameter(Mandatory = $false,
                   Position  = 1)]
        [Alias('Exit')]
        [boolean]
        $exitScript = $false
    )
    
    $time = Get-Date
    
    if (Test-Path $logPath) {
        
        $flairLength = ("Finished processing at [$time]").Length + 1
        
        Write-Verbose "Adding the following content to: $logPath"
        Write-Verbose ('-'*$flairLength)
        Write-Verbose "Ended logging at [$time]"
        Write-Verbose ('-'*$flairLength)
        Write-Verbose ""
        
        Add-Content -Path $logPath -Value ('-'*$flairLength)
        Add-Content -Path $logPath -Value "Ended logging at [$time]"
        Add-Content -Path $logPath -Value ('-'*$flairLength)
   
    } else {
        
        Write-Error "Unable to access $logPath"

        Break
        
    }
    
    if ($exitScript) {
        
        Write-Verbose "Exiting on completion specified, exiting..."
        
        Exit
        
    } 
   
}

function Out-LogFile {
<#
.SYNOPSIS
   Out-LogFile will create, add to, and resolve a logfile.

.DESCRIPTION
   Out-LogFile will create, add to, and resolve a logfile.

   Value from the pipeline is accepted.

   Use the -Verbose parameter to display what is happening to the host.

.PARAMETER logPath
    Alias: Path
    Type : String

    Specify the path to the logFile you'd like created.

.PARAMETER logName    
    Alias: Name
    Type : String

    Specify the name of the log file. Be sure to include the extension if specifying the name.

.PARAMETER logValue
    Alias: Value
    Type : String

    Specify the value(s) you'd like logged.

.PARAMETER addTimeStamp
    Type : Boolean

    Defaults to true, set to false if you'd like to omit the timestamp.

.NOTES
    Name: Out-LogFile
    Version: 1.0
    Author: Ginger Ninja (Mike Roberts)
    DateCreated: 5/11/16
    
.LINK
    http://www.gngrninja.com

.EXAMPLE
    $outLog = Get-Process | Out-LogFile -logPath c:\temp -logName 'outlog.log' -Verbose 
    -----------------------------
    
    VERBOSE: Created c:\temp\outlog.log
    VERBOSE:
    VERBOSE: File C:\temp\outlog.log created and verified to exist.
    VERBOSE:
    VERBOSE: Adding the following information to: C:\temp\outlog.log
    VERBOSE:
    VERBOSE: -----------------------------------------------------------------
    VERBOSE: Started logging at [05/11/2016 16:58:43]
    VERBOSE: Script (Version 0.1) executed by: [thegn] on computer: [GINJA10]
    VERBOSE: -----------------------------------------------------------------
    VERBOSE:
    VERBOSE: Adding [05-11-16 16:58:43] System.Diagnostics.Process (AdobeUpdateService) to
    C:\temp\outlog.log
    VERBOSE:

.EXAMPLE
    $outLog = Get-Process | Out-LogFile -Verbose
    -----------------------------
    
    gngrNinja> more $outLog
    ...
    [05-11-16 16:56:02] System.Diagnostics.Process (winlogon)

    [05-11-16 16:56:02] System.Diagnostics.Process (WmiPrvSE)

    [05-11-16 16:56:02] System.Diagnostics.Process (WmiPrvSE)

    [05-11-16 16:56:02] System.Diagnostics.Process (WUDFHost)

    ---------------------------------------------
    Ended logging at [05/11/2016 16:56:02]
    ---------------------------------------------

gngrNinja>
#>
    [cmdletbinding()]
    param(
        [Parameter(Mandatory = $false,
                   Position  = 0)]
        [Alias('Path')]
        [string]
        $logPath,
        [Parameter(Mandatory = $false,
                   Position  = 1)]
        [Alias('Name')]
        [string]
        $logName,
        [Parameter(Mandatory = $true,
                   ValueFromPipeLine               = $true,
                   ValueFromPipelineByPropertyName = $true,
                   Position                        = 2)]
        [Alias('Value')]
        [string]
        $logValue,
        [Parameter(Mandatory = $false,
                   Position  = 3)]
        [boolean]
        $addTimeStamp = $true
    )
    
    Begin {
        
        $logFile = New-LogFile -logPath $logPath -logName $logName

    }
    
    Process {

        ForEach ($value in $logValue) {
        
            Write-LogFile -logPath $logFile -logValue $value -addTimeStamp $addTimeStamp
        
        }


    }
    
    End {
    
        Resolve-LogFile $logFile 

        Return $logFile

    }
    
}

function Send-LogEmail {
    [cmdletbinding()]
    param(
        [Parameter(Mandatory=$true)]
        [string]
        $To,
        [Parameter(Mandatory=$true)]
        [string]
        $Subject,
        [Parameter(Mandatory=$true)]
        [string]
        $Body,
        [Parameter(Mandatory=$true)]
        [string]
        $emailFrom,
        [Parameter(Mandatory=$true)]
        [string]
        $emailUser,
        [Parameter(Mandatory=$false)]
        [string]
        $provider = 'gmail',
        [Parameter(Mandatory=$true)]
        $password = (Read-Host "Password?" -AsSecureString)
    )

    if (!$to)        {Write-Error "No recipient specified";break}
    if (!$subject)   {Write-Error "No subject specified";break}
    if (!$body)      {Write-Error "No body specified";break}
    if (!$emailFrom) {$emailFrom = 'Ninja_PS_Logging@gngrninja.com'}
   
    Switch ($provider) {

        {$_ -eq 'gmail'} {
            
            $SMTPServer   = "smtp.gmail.com"
            $SMTPPort = 587

        }

        {$_ -eq 'custom'} {

            $SMTPServer = 'Your.SMTP.Server'
            $SMTPPort   = 'Your.SMTP.Server.Port'

        }

    }

    $gmailCredential = New-Object System.Management.Automation.PSCredential($emailUser,$password)
    Send-MailMessage -To $to -From $emailFrom -Body $body -BodyAsHtml:$true -Subject $Subject -SmtpServer $smtpServer -Port $smtpPort -UseSsl -Credential $gmailCredential

}

Notes:

  • I've spent some time on the help with the NinjaLogging.psm1 module
    • Feel free to browse through and use it as you wish, let me know if you have any problems!
  • I have not finished the email sending feature
  • This code will eventually find its way to Github, and be a lot more polished

Homework

  • Find an API for an application you'd like control with PowerShell
    • Study up on the documentation, and find ways to send data to it
    • Automate something using what you've learned today

[Back to top]