Powershell con intercambio- ¿Cómo añado todo el resultado detallado a un archivo?

Estoy tratando de agregar algo de inicio de sesión en mi script. Cualquier consejo sería muy apreciado. Para comenzar, me gustaría agregar un registro de errores cuando algo va mal.

Por ejemplo, cuando un usuario no puede ser encontrado, se produce el siguiente error:

La operación no se pudo realizar porque el objeto ‘asdfa’ no se pudo encontrar en ‘HQ-DC-6.domain.com’. + CategoryInfo: NotSpecified: (0: Int32) [Set-RemoteMailbox], ManagementObjectNotFoundException + FullyQualifiedErrorId: 47285FC7, Microsoft.Exchange.Management.RecipientTasks.SetRemoteMailbox + PSComputerName: hq-cas2.domain.com

===========================

$users = ForEach ($user in $(Get-Content 'C:\Users\test\Documents\Powershell Scripts\OffboardUsers.txt')) { $tmpname = Get-RemoteMailbox -Identity $user | Select-Object -ExpandProperty Name $tmpDisplayName = Get-RemoteMailbox -Identity $user | Select-Object -ExpandProperty DisplayName Set-RemoteMailbox -Identity $user -Name ("_" + "$tmpname") >> error.log Set-RemoteMailbox -Identity $user -DisplayName ("_" + "$tmpDisplayName") >> error.log Set-RemoteMailbox -Identity $user -HiddenFromAddressListsEnabled $true >> error.log } 

Este artículo está completamente revisado 2017-07-18 ya que la nueva solución Log-Entry reemplaza a la anterior solución Write-Log que no se actualizará más. Ver también: Migración desde Write-Log .


En general, considero que el registro está subestimado para los lenguajes de scripting de Microsoft. No solo en el momento del diseño de un script (o cmdlet), el registro es útil, pero cuando se despliega el script y algo va mal, a menudo deseamos que haya iniciado sesión mucho mejor.
Es por eso que creo que los lenguajes de scripting como PowerShell (así como su predecesor VBScript) en realidad deberían venir con capacidades de registro nativas más sofisticadas que las que están disponibles ahora.

Mejores prácticas

Incluso antes de que existiera PowerShell, tenía una necesidad similar de una función de registro adecuada en VBScript. Como cuestión de hecho, algunos de los conceptos que estaba usando para VBScript, todavía estoy usando en PowerShell. Mientras tanto, extendí mi solución de registro con una lista completa de mejoras y requisitos, ya que espero que una función de registro sea:

  • Robusto y nunca causa que el cmdlet real falle inesperadamente (incluso
    cuando, por ejemplo, se niega el acceso al archivo de registro)

  • Simple de invocar y posiblemente ser utilizado como un
    Reemplazo del comando Write-Host

  • Resuelva todos los tipos de datos y revele el contenido

  • Captura errores de script nativos inesperados

  • Capaz de pasar objetos por el registro en línea para minimizar líneas de código adicionales

  • Tener una marca de tiempo precisa (10 ms) por entrada para problemas de rendimiento
    disparo

  • Estándar capturando información de resolución de problemas como:

    • Versión de script

    • Versión de PowerShell

    • Cuando se ejecutó (tiempo de inicio del proceso)

    • Cómo (parámetros) y desde dónde (ubicación) se ejecutó

  • Se agrega información adjunta a un archivo de registro configurable que no crece indefinidamente

  • Hacia abajo compatible con la versión 2 de PowerShell

Robusto

Si desea obtener una solución de registro sólida, es probable que desee ir con el cmdlet nativo de Start-Transcript, pero probablemente descubra que Start-Transcript carece de características, como las marcas de tiempo, que podría esperar de un cmdlet de registro apropiado. Puede optar por una solución de terceros, pero esto generalmente implica procedimientos y dependencias adicionales de instalación.
Entonces decide escribirlo usted mismo, pero incluso la solución más simple en la que simplemente escribe información en un archivo puede causar un problema en el campo: es posible que no se pueda acceder al archivo. Incluso podría existir, pero su secuencia de comandos se desencadena dos veces y se ejecutan varias instancias al mismo tiempo que una de las instancias puede abrir el archivo de registro y se deniega el acceso desde la otra instancia (consulte, por ejemplo: conflictos de Powershell Scheduled Tasks? ). Y justo en este punto, el registro en realidad debería ayudarlo a solucionar lo que está sucediendo, ya que un activador repetitivo también podría provocar un comportamiento inesperado en el script. Para este ejemplo en particular, la solución que presento aquí almacena temporalmente la salida hasta que sea capaz de escribir. Pero hay muchas más trampas al escribir un cmdlet de registro y formatear correctamente el resultado.

Entrada de registro

He puesto toda la solución en un marco Log-Entry.ps1 que consta de algunas partes principales:

  1. Un encabezado de ayuda y una plantilla de función Main con algunos ejemplos
  2. A My objeto que contiene algunas definiciones de secuencia de comandos y registro
  3. Cuatro funciones para controlar el registro:
    • Log-Entry (alias Log ) para registrar información y objetos
    • Set-LogFile (alias LogFile ) para establecer la ubicación del archivo de registro
    • End-Script (alias End ) que podría usarse para cerrar bien la sesión
    • ConvertTo-Text (alias CText ) para resolver objetos

Para obtener la última versión de Log-Entry.ps1 , consulte: https://github.com/iRon7/Log-Entry .

Uso

Descargue el framwork anterior Log-Entry.ps1 y reemplace los ejemplos en la función Main {} con su propio script. Donde quiera que quiera mostrar y registrar información, use el comando Log (similar a la syntax del comando Write-Host ).
Ejecute el script y verifique el archivo de registro en: %Temp%\.Log

Sintaxis

Para obtener detalles sobre la syntax, consulte: el archivo readme.md en https://github.com/iRon7/Log-Entry

Ejemplo

Aquí hay algunos comandos que muestran algunas de las características del marco de Log-Entry :

 LogFile .\Test.log # Redirect the log file location (Optional) Log -Color Yellow "Examples:" Log "Several examples that usually aren't displayed by Write-Host:" $NotSet @() @(@()) @(@(), @()) @($Null) Log -Indent 1 "Note 1: An empty string:" "" "isn't displayed by Log-Entry either (as you usually do not want every comment quoted)." Log -Indent 2 "In case you want to reveal a (possible) empty string, use -QuoteString:" -NoNewline; Log -QuoteString "" Log -Indent 1 "Note 2: An empty array embedded in another array:" @(@()) "is flattened by PowerShell (and not Write-Log)." Log -Indent 2 "To prevent this use a comma in front of the embbed array: " @(,@()) Log "A hashtable:" @{one = 1; two = 2; three = 3} Log "A recursive hashtable:" @{one = @{one = @{one = 1; two = 2; three = 3}; two = 2; three = 3}; two = 2; three = 3} -Expand -Depth:9 Log "Character array:" "Hallo World".ToCharArray() Log-Verbose "The following line produces a error which is captured in the log file:" $File = Log "File:" (Get-ChildItem "C:\NoSuchFile.txt" -ErrorAction SilentlyContinue) Log-Verbose "The switch -FlushErrors prevents the error being logged:" $File = Log "File:" (Get-ChildItem "C:\NoSuchFile.txt" -ErrorAction SilentlyContinue) -FlushErrors Log "Below are two inline log examples (the object preceding the ""?"" is returned):" $Height = Log "Height:" 3 ? "Inch" $Width = Log "Width:" 4 ? "Inch" Log-Verbose "Or one display/log line spread over multiple code lines:" Log "Periphery:" -NoNewline $Periphery = Log (2 * $Height + 2 * $Width) ? -Color Green -NoNewline Log "Inch" Log-Debug "Password:" $Password "(This will not be shown and captured unless the common -Debug argument is supplied)" 

Monitor

Los comandos de ejemplo se muestran en el siguiente formato:

Monitor

Archivo de registro

Los comandos de ejemplo registran la siguiente información en el archivo de registro:

 2017-07-13 PowerShell version: 5.1.15063.483, process start: 2017-07-13 15:39:44 15:39:46.75 Log-Entry version: 02.00.01, command line: C:\Users\User\Scripts\Log-Entry\Log-Entry.ps1 15:39:46.80 Examples: 15:39:46.94 Several examples that usually aren't displayed by Write-Host: $Null @() @() @(@(), @()) @($Null) 15:39:46.95 Note 1: An empty string: isn't displayed by Log-Entry either (as you do not want every comment quoted). 15:39:46.98 In case you want to reveal a (possible) empty string, use -QuoteString: "" 15:39:47.00 Note 2: An empty array embedded in another array: @() is flattened by PowerShell (and not Write-Log). 15:39:47.01 To prevent this use a comma in front of the embbed array: @(@()) 15:39:47.05 A hashtable: @{one = 1, three = 3, two = 2} 15:39:47.06 A recursive hashtable: @{ one = @{ one = @{ one = 1, three = 3, two = 2 }, three = 3, two = 2 }, three = 3, two = 2 } 15:39:47.10 Character array: @(H, a, l, l, o, , W, o, r, l, d) 15:39:47.11 The following line produces a error which is captured in the log file: Error at 51,23: Cannot find path 'C:\NoSuchFile.txt' because it does not exist. 15:39:47.15 File: $Null 15:39:47.16 The switch -FlushErrors prevents the error being logged: 15:39:47.17 File: $Null 15:39:47.17 Below are two inline log examples (the object preceding the "?" is returned): 15:39:47.18 Height: 3 Inch 15:39:47.19 Width: 4 Inch 15:39:47.19 Or one display/log line spread over multiple code lines: 15:39:47.20 Periphery: 14 Inch 15:39:47.27 End (Execution time: 00:00:00.5781145, Process time: 00:00:03.1067112) 

Puede usar la función Write-Log. Esa sería la mejor manera en tu caso, creo.

Esta función ha sido escrita por Jason Wasser:

 <# .Synopsis Write-Log writes a message to a specified log file with the current time stamp. .DESCRIPTION The Write-Log function is designed to add logging capability to other scripts. In addition to writing output and/or verbose you can write to a log file for later debugging. .NOTES Created by: Jason Wasser @wasserja Modified: 11/24/2015 09:30:19 AM Changelog: * Code simplification and clarification - thanks to @juneb_get_help * Added documentation. * Renamed LogPath parameter to Path to keep it standard - thanks to @JeffHicks * Revised the Force switch to work as it should - thanks to @JeffHicks To Do: * Add error handling if trying to create a log file in a inaccessible location. * Add ability to write $Message to $Verbose or $Error pipelines to eliminate duplicates. .PARAMETER Message Message is the content that you wish to add to the log file. .PARAMETER Path The path to the log file to which you would like to write. By default the function will create the path and file if it does not exist. .PARAMETER Level Specify the criticality of the log information being written to the log (ie Error, Warning, Informational) .PARAMETER NoClobber Use NoClobber if you do not wish to overwrite an existing file. .EXAMPLE Write-Log -Message 'Log message' Writes the message to c:\Logs\PowerShellLog.log. .EXAMPLE Write-Log -Message 'Restarting Server.' -Path c:\Logs\Scriptoutput.log Writes the content to the specified log file and creates the path and file specified. .EXAMPLE Write-Log -Message 'Folder does not exist.' -Path c:\Logs\Script.log -Level Error Writes the message to the specified log file as an error message, and writes the message to the error pipeline. .LINK https://gallery.technet.microsoft.com/scriptcenter/Write-Log-PowerShell-999c32d0 #> function Write-Log { [CmdletBinding()] Param ( [Parameter(Mandatory=$true, ValueFromPipelineByPropertyName=$true)] [ValidateNotNullOrEmpty()] [Alias("LogContent")] [string]$Message, [Parameter(Mandatory=$false)] [Alias('LogPath')] [string]$Path='C:\Logs\PowerShellLog.log', [Parameter(Mandatory=$false)] [ValidateSet("Error","Warn","Info")] [string]$Level="Info", [Parameter(Mandatory=$false)] [switch]$NoClobber ) Begin { # Set VerbosePreference to Continue so that verbose messages are displayed. $VerbosePreference = 'Continue' } Process { # If the file already exists and NoClobber was specified, do not write to the log. if ((Test-Path $Path) -AND $NoClobber) { Write-Error "Log file $Path already exists, and you specified NoClobber. Either delete the file or specify a different name." Return } # If attempting to write to a log file in a folder/path that doesn't exist create the file including the path. elseif (!(Test-Path $Path)) { Write-Verbose "Creating $Path." $NewLogFile = New-Item $Path -Force -ItemType File } else { # Nothing to see here yet. } # Format Date for our Log File $FormattedDate = Get-Date -Format "yyyy-MM-dd HH:mm:ss" # Write message to error, warning, or verbose pipeline and specify $LevelText switch ($Level) { 'Error' { Write-Error $Message $LevelText = 'ERROR:' } 'Warn' { Write-Warning $Message $LevelText = 'WARNING:' } 'Info' { Write-Verbose $Message $LevelText = 'INFO:' } } # Write log entry to $Path "$FormattedDate $LevelText $Message" | Out-File -FilePath $Path -Append } End { } } 

Uso:

  Write-Log -Message 'Folder does not exist.' -Path c:\Logs\Script.log -Level Error Write-Log -Message 'Restarting Server.' -Path c:\Logs\Scriptoutput.log 

Nota: Puede usar un get-help para esta función siempre para todos los detalles.

Espero eso ayude.

    Intereting Posts