используйте пользовательскую функцию для ведения журнала из invoke-command

#powershell

#powershell

Вопрос:

Я знаю, что есть много тем на эту тему, но я не смог найти решение своей проблемы, поэтому я действительно мог бы использовать руководство (возможно, я что-то упускаю).

У меня есть пользовательская функция для записи данных в файл журнала:

 function Write-Log 
{  

[CmdletBinding()] 
Param 
( 
    [Parameter(Mandatory=$true, 
               ValueFromPipelineByPropertyName=$true)] 
    [ValidateNotNullOrEmpty()] 
    [Alias("LogContent")] 
    [string]$Message, 

    [Parameter(Mandatory=$false)] 
    [Alias('LogPath')] 
    [string]$Path='C:Scriptsdefault.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' { 
            # If you want to write INFO messages to console, uncomment line below.
            #Write-Verbose $Message 
            $LevelText = 'INFO:' 
            } 
        } 
     
    # Write log entry to $Path 
    "$FormattedDate (PID: $PID) $LevelText $Message" | Out-File -FilePath $Path -Append 
} 
End 
{ 
} 
}
  

Что я пытаюсь сделать, так это выполнить некоторые команды удаленно, invoke-command и я хотел бы зарегистрировать (локально) некоторые действия, выполняемые на удаленном компьютере. Примером команд, вызываемых удаленно, является:

 $DriveLetter = Invoke-Command -ComputerName computer2 -ScriptBlock {
         

            try {
                    Write-Log "Trying to mount the profile on the remote server..." -Level Info -Path $log_path
                    $path_to_mount = $args[0]   ""   $args[1]
                    $DriveLetter = ((Mount-VHD -Path $path_to_mount -ErrorAction Stop -PassThru| Get-Disk | Get-Partition | Get-Volume).DriveLetter) ":"
                    Write-Log "Profile mounted to $DriveLetter" -Level Info  -Path $log_path

                    return $DriveLetter
                }
                catch [Microsoft.HyperV.PowerShell.VirtualizationException] {
                Write-Host "Profile is mounted." -ForegroundColor Red
                Write-Log "Profile is mounted." -Level Error  -Path $log_path
                
            } -ConfigurationName ConnectUPD -ArgumentList $sourceDir,$upd_file -ErrorAction Stop
  

Приведенный выше пример просто монтирует (на компьютере 2) файл (с компьютера 3) и возвращает его букву диска (на компьютере 1 выполняется скрипт). Если я закомментирую write-log строки, код выполняется безупречно.

Я попытался применить множество решений, предлагаемых онлайн (например, это, которое работает нормально, но только в том случае, если блок сценария содержит только функцию. Если в блоке сценария больше материала, как в моем случае, он не работает). самое большее, что я получил, это заставить удаленный компьютер регистрировать действия, но в новый удаленный файл на его диске на C, а не локально, где выполняется скрипт.

Мне действительно может понадобиться помощь и рекомендации по этой проблеме. Это надирает мне задницу: (

Заранее спасибо.

Комментарии:

1. Ожидаете ли вы Write-Log в удаленном сеансе записи C:scriptsdefault.log на локальном / вызывающем компьютере?

2. Нет, извините, что не прояснил это. По умолчанию. журнал — это просто местоположение по умолчанию на случай, если при вызове функции не отправляется путь. Но, как вы можете видеть, я вызываю функцию с аргументом $log_path (который берется из другой части кода, которую я не включил, но он доступен для блока invoke-command). Я хочу, чтобы удаленный компьютер (computer2) регистрировал данные в $log_path, который находится на computer1 (откуда запускается скрипт)

Ответ №1:

Чтобы сделать то, что вы пытаетесь сделать, я бы перенес функцию ведения журнала из локальной области на удаленную, выполнил все необходимое, сгенерировал файлы журналов на удаленных компьютерах, а затем вернулся к существующим сеансам и извлек файлы локально.

Вот пример adhoc, который я только что придумал (подсветка синтаксиса SO не понравилась комментариям блока, поэтому, пожалуйста, извините за отсутствие)

 
# Function creation, screw foo, I use banana
function banana() 
{
    # to let you know that the function is working
    Write-Host "From function"
    # creates a file to copy
    New-Item -Path "C:tempremotefunctest.txt" -Force
}


# This creates an object that is a representation of the function in string form
# this is what we'll dot source from within the invoke's scope
$myFunction = "function banana {${function:banana}}"

# list of your machines 
# for simplicity I just built a string array, you can use whatever method you deem best
[string[]]$listOfComputerNames = @("machine1","machine2")

# Empty session array instantiation
[System.Management.Automation.Runspaces.PSSession[]]$sessions = @()

# iterate through machine names and creates sessions, adding them to the sessions array
foreach($name in $listOfComputerNames)
{
    $sessions  = New-PSSession -ComputerName $name # add -Credential if needed
}

# Will invoke your commands against the persistent sessions,
# instantiating the function, and running said function,
# which creates the file for you
Invoke-Command -Session $sessions -ScriptBlock {

    
    # This will create a new ScriptBlock object, 
    # then use dot sourcing to run that scriptblock inside the current scope. 
    # It's the exact same functionality as copy-pasting the entire function 
    # into the invoke command
    
    . $([scriptblock]::Create($using:myFunction))
    
    # this is calling the function
    banana

    Write-Host "From invoke"


}


    # This will iterate through the $sessions array,
    # copy the designated file via Copy-Item's
    # fancy schmancy -FromSession parameter,
    # then place that file in a folder that is 
    # named after the name of the remote machine.

foreach($session in $sessions)
{
    
    # With the -FromSession ParameterSet, 
    # -Path is the file path on the REMOTE machine to copy the file FROM
    # -Destination is the file path on the LOCAL machine to copy the file TO
    
    Copy-Item -FromSession $session -Path "C:tempremotefunctest.txt" -Destination "C:tempsession-copy-test$($session.ComputerName)"
    
}

# Clean up the sessions
Remove-PSSession $sessions

  

Комментарии:

1. вы имеете в виду, чтобы удаленный компьютер регистрировал данные локально, затем с локального компьютера получал доступ к удаленному компьютеру и получал зарегистрированную информацию в исходный файл журнала? не было бы просто просто указать удаленному компьютеру путь UNC, указывающий на исходный файл журнала первого компьютера? например, \computer1logslog.text