Write-AzLogEntry.psm1

<#PSScriptInfo
.SYNOPSIS
  Log a message with the specified log level in output or eventlog or log file and optional Storage Account Blob
.DESCRIPTION
  Log a message with the specified log level and additional detail like date+time, line number and message in
  output console or eventlog or log file, and an optional upload to a blob within an Azure Storage Account.

  Write Messages to all Streams, as well as simultaneously to EventLog and/or a Log File and/or Output to screen
  through all Levels, and ability to update a Blob Storage block with the data on each log write.
.NOTES
  ┌─┐┬─┐┌─┐┌┬┐┌─┐┬─┐┌─┐ ┌┬┐┌─┐┌─┐┬ ┬┌┐┌┌─┐┬ ┌─┐┌─┐┬┌─┐┌─┐ ┬ ┬ ┌─┐
  ├─┘├┬┘│ │ │ ├┤ ├┬┘├─┤ │ ├┤ │ ├─┤││││ ││ │ ││ ┬│├┤ └─┐ │ │ │
  ┴ ┴└─└─┘ ┴ └─┘┴└─┴ ┴ ┴ └─┘└─┘┴ ┴┘└┘└─┘┴─┘└─┘└─┘┴└─┘└─┘ ┴─┘┴─┘└─┘
  # Copyright (c) Protera Technologies LLC. All rights reserved. #

  Author Cody Diehl|Protera Technologies LLC
  Email c.diehl@protera.com
  Create 05-14-2022 02:09:03
  Modify 05-26-2022 09:12:43
  File Name Write-AzLogEntry.psm1
  Version 0.9.0

  05/18/2022 - First Deployment and testing - Cody Diehl
  05/26/2022 - Updated to Module and publish to Powershell Gallery - Cody Diehl

.EXAMPLE
  # Write-AzLogEntry to Log File Only on the Warning Log Stream
  Write-AzLogEntry -Level "Warning" -LogPrefix "$env:TEMP/TestLog" -Message "Warning Msg test"
.EXAMPLE
  # Write-AzLogEntry to Eventlog on Information Stream Level
  Write-AzLogEntry -Eventlog -LogPrefix "TestEventLog" -Message "Info EventLog Msg Test"
.EXAMPLE
  # Write-AzLogEntry to Output Screen as well as Log File on Verbose Stream
  Write-AzLogEntry -Level "Verbose" -Out -LogPrefix "TestLogAndOutput" -Message "Verbose Output and Log Entry"
.EXAMPLE
  # Write-AzLogEntry to Output, Log and Upload it to Storage Account Blob
  Write-AzLogEntry -Output -LogPrefix "BlobLogTest" -Message "Output this data to screen, log it, and upload to storage account" -RG "azure-storage-us-east-dev" -Account "blobstoageacct" -Container "Logs"
.EXAMPLE
  # Write-AzLogEntry take Message from pipeline input and Write it to Log File and Output to Screen
  $SomeValue | Write-AzLogEntry -Log "VariableLog" -Output
.PARAMETER Message
  Mandatory
  The content which will written to the desired log level (Information, by Default)
  Can also be taken from Pipeline Input
.PARAMETER LogPrefix
  Aliases: Log
  Mandatory
  The log file name will be '$LogPrefix-MM-dd-YY.log'
.PARAMETER Level
  Optional, defaults to Information
  The Log Stream level in which to write your Message content to (Verbose, Information, Warning, Error or Debug)
  Note: All Log levels are set to continue, so if you want them to fail your script pass the -ErrorAction,
         -VerboseAction, -DebugPreference global flags to achieve this functionality and override the script
.PARAMETER Output
  Aliases: Out
  Optional, if not passed will only write log file
  Will also output to console if passed
.PARAMETER Eventlog
  Optional, if not passed will only write log file
  Write your log entry to the EventLog if passed
.PARAMETER ResourceGroupName
  Aliases: RG, ResourceGroup, RGName
  Optional, but needed if you want to upload to blob within a Storage Account
  Resource Group Name of a Storage Account
.PARAMETER StorageAccountName
  Aliases: SA, Account
  Optional, but needed if you want to upload to blob within a Storage Account
  Storage Account Name
.PARAMETER StorageContainer
  Aliases: SC, Container
  Optional, but needed if you want to upload to blob within a Storage Account
  Storage Account Container Name in which to upload a blob of the Log file
#>


function Write-AzLogEntry() {
  [CmdletBinding()]
  param(
    [Parameter(Mandatory = $true)][Alias('Log')][String]$LogPrefix,
    [Parameter(Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelinebyPropertyName = $true)][String]$Message,
    [Parameter(Mandatory = $false)][Alias('Out')][Switch]$Output,
    [Parameter(Mandatory = $false)][Switch]$Eventlog,
    [Parameter(Mandatory = $false)]
    [ValidateSet('Verbose', 'Information', 'Warning', 'Error', 'Debug')]
    [String]$Level = 'Information',
    [Parameter(Mandatory = $false, ParameterSetName = 'Blob')][Alias('RG', 'ResourceGroup', 'RGName')][String]$ResourceGroupName,
    [Parameter(Mandatory = $false, ParameterSetName = 'Blob')][Alias('SA', 'Account')][String]$StorageAccountName,
    [Parameter(Mandatory = $false, ParameterSetName = 'Blob')][Alias('SC', 'Container')][String]$StorageContainer
  )

  # Message Line Formatting
  [String]$MessageTimeStamp = (Get-Date).ToString('yyyy-MM-dd HH:mm:ss')
  $Msg = "[$($MessageTimeStamp)] - [Line:$($MyInvocation.ScriptLineNumber)$(if ($false -ne $(Split-Path $MyInvocation.PSCommandPath -Leaf)) { Write-Output "|Script:$(Split-Path $MyInvocation.PSCommandPath -Leaf)" })] - $Level`: $Message"

  # Output to EventLog
  if ($Eventlog -eq $true) {
    if ($Level -eq 'Verbose') {
      $EntryType = 'Information'
    }
    else {
      $EntryType = $Level
    }
    Write-EventLog -LogName 'Windows PowerShell' -Source 'PowerShell' -EventId 0 -Category 0 -EntryType $EntryType -Message $Msg
  }

  # Option to also Log the message to the Console as well
  if ($Output) {
    switch ($Level) {
      'Verbose' { Write-Verbose -Message $Msg -Verbose }
      'Information' { try { Write-Output $Msg } catch { Write-Host $Msg } }
      'Warning' { Write-Warning -Message $Msg -WarningAction Continue }
      'Error' { Write-Error -Message $Msg -ErrorAction Continue }
      'Debug' { Write-Debug -Message $Msg -Debug }
    }
  }

  if ($null -ne $LogPrefix) {

    # Log File Name
    $TodayDate = (Get-Date).ToString('MM-dd-yy')
    $LogFull = "$LogPrefix-$TodayDate.log"
    #$null = New-Item -ItemType File -Name $LogFull

    # Write initial entry or create log file.
    $Msg | Out-File -FilePath $LogFull -Append

    # If the storage account params are passed then will update the storage account location per run.
    if (($null -ne $StorageAccountName -and $StorageAccountName -ne 0 -and $StorageAccountName -ne '' -and $StorageAccountName -ne $false) -or
        ($null -ne $StorageAccountKey -and $StorageAccountKey -ne 0 -and $StorageAccountKey -ne '' -and $StorageAccountKey -ne $false) -or
        ($null -ne $StorageContainer -and $StorageContainer -ne 0 -and $StorageContainer -ne '' -and $StorageContainer -ne $false)) {
      Write-Output 'Uploading Log File'
      # Get key to storage account
      $AccountKey = (Get-AzureRmStorageAccountKey -Name "$StorageAccountName" -ResourceGroupName "$ResourceGroupName").Value[0]
      # Map to the reports BLOB context
      $StorageContext = New-AzureStorageContext -StorageAccountName "$StorageAccountName" -StorageAccountKey "$AccountKey"
      # Copy the file to the storage account
      Set-AzureStorageBlobContent -File "$LogFull" -Container "$StorageContainer" -BlobType 'Block' -Context "$StorageContext" -Verbose
    }
  }
}

Export-ModuleMember -Function Write-AzLogEntry