简体   繁体   中英

use custom function for logging from invoke-command

I know there is a lot of topics with this subject but I couldn't find a solution for my issue so i could really use a guidance (maybe i'm missing something).

I have a custom function to write stuff to a log file:

function Write-Log 
{  

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

    [Parameter(Mandatory=$false)] 
    [Alias('LogPath')] 
    [string]$Path='C:\Scripts\default.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 
{ 
} 
}

What i'm trying to do is to run some commands remotely with invoke-command and I would like to log (locally) some of the activity done on the remote computer. An example for the commands invoked remotely is:

$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

The example above simply mounts(on computer2) a file (from computer3) and returns its drive letter (computer1 is where the script runs). If i comment out the write-log lines, the code runs flawlessly.

I tried applying a lot of solutions offered online (like this one which works fine but only if the script block contains only the function. If the script block has more stuff, like in my case, it doesn't work). the furthest I got is to make the remote computer log the actions but to a new remote file on its on C drive instead of locally where the script runs.

I could really need some help and guidance which this issue. Its kicking my ass:(

Thanks in advance.

To do what you're trying to do, I would, dot sourcing the log function from the local scope to the remote, do the stuff, generate the log files on the remote machines, then afterwards, go back into the existing sessions, and pull the files down locally.

Here's an adhoc example I just came up with (SO's syntax highlighting didn't like block comments, so please excuse the lack)


# 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:\temp\remotefunctest.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:\temp\remotefunctest.txt" -Destination "C:\temp\session-copy-test\$($session.ComputerName)\"
    
}

# Clean up the sessions
Remove-PSSession $sessions

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM