Public/Copy-HDTLog.ps1
|
function Copy-HDTLog { <# .SYNOPSIS Copies the log directory back to the share, on phase end and on failure. .DESCRIPTION On phase end and on failure the log directory is copied to <share>\Logs\<ComputerName>-<RunId>\. "Copy-back happens on failure too - a deployment that dies is exactly when the logs matter, and MDT's habit of stranding them on a wiped machine is a real operational problem." IT NEVER THROWS. A share that has gone away is the normal case for a machine that failed early, and a copy-back that threw would mask the failure it was called to preserve evidence of. A failure is logged as a Warning and the function returns nothing, so a caller in a finally block can call it unguarded. The directory structure under the log path is preserved, so Steps\003-ApplyImage.log arrives as Steps\003-ApplyImage.log rather than being flattened into a directory of clashing names. Everything goes through the context's injected IFileSystem. Children are classified with GetLength, which succeeds for a file and throws for a directory on both implementations of IFileSystem - the interface has no "is this a directory" method, and adding one for this would be a wider change than the question deserves. .PARAMETER Context A New-HDTLogContext result. Supplies the log path, the run id and the injected services. .PARAMETER Destination The share's log root, conventionally <share>\Logs. .PARAMETER ComputerName The name of the machine being deployed, which the engine passes from %HDTComputerName%. Defaults to this machine's name for a caller that has not resolved one yet. .OUTPUTS System.String - the directory the logs were copied into, or nothing when the copy failed. .EXAMPLE Copy-HDTLog -Context $context -Destination '\\share\Logs' -ComputerName $computerName \\share\Logs\PC-0001-8f3c1a90, with the whole log tree inside it. #> [CmdletBinding()] [OutputType([string])] param( [Parameter(Mandatory = $true, Position = 0)] [ValidateNotNull()] [object] $Context, [Parameter(Mandatory = $true, Position = 1)] [ValidateNotNullOrEmpty()] [string] $Destination, [Parameter()] [ValidateNotNullOrEmpty()] [string] $ComputerName = [System.Environment]::MachineName ) Set-StrictMode -Version Latest $ErrorActionPreference = 'Stop' $fileSystem = $Context.FileSystem $root = '{0}\{1}-{2}' -f $Destination.TrimEnd('\', '/'), $ComputerName, $Context.RunId $source = [string] $Context.LogPath try { $fileSystem.CreateDirectory($root) # Breadth-first over the log tree, carrying each item's path relative to # the log root so the structure survives the copy. $pending = New-Object -TypeName System.Collections.ArrayList [void] $pending.Add([pscustomobject] @{ Path = $source; Relative = '' }) while ($pending.Count -gt 0) { $current = $pending[0] $pending.RemoveAt(0) foreach ($child in @($fileSystem.GetChildItem($current.Path))) { $leaf = Split-Path -Path $child -Leaf $relative = $leaf if (-not [string]::IsNullOrEmpty([string] $current.Relative)) { $relative = '{0}\{1}' -f $current.Relative, $leaf } $isFile = $true try { [void] $fileSystem.GetLength($child) } catch { $isFile = $false } if ($isFile) { $fileSystem.CopyItem($child, ('{0}\{1}' -f $root, $relative)) } else { [void] $pending.Add([pscustomobject] @{ Path = $child; Relative = $relative }) } } } } catch { Write-HDTLog -Context $Context -Severity Warning -Component 'Logging' ` -Message ("Could not copy the deployment logs to '{0}': {1}" -f $Destination, $_.Exception.Message) return } return $root } |