Out-File not working with Invoke-Command

This topic contains 10 replies, has 4 voices, and was last updated by Profile photo of Daniel Krebs Daniel Krebs 2 months, 2 weeks ago.

  • Author
    Posts
  • #54269
    Profile photo of Brandon Lashmet
    Brandon Lashmet
    Participant

    I have the following ScriptBlock:

    $ScriptBlock =  { ForEach($WebSite in $(get-website))
                {
                    $ReportFile = "\\FileShare\WebLogs\FreeSpaceReport.csv"
                    $LogLocation="$($Website.logFile.directory)\w3svc$($website.id)".replace("%SystemDrive%",$env:SystemDrive)
                    $LogDrive = (get-item $LogLocation).psdrive.name
                    $LogDriveFreeSpace = (get-psdrive $LogDrive).free/1gb
                    $LogDriveFreeSpace = "{0:N2}" -f $LogDriveFreeSpace
                    $Output = "$server $($WebSite.name) [$LogLocation] $LogDriveFreeSpace"
                    $Output | Out-File $ReportFile -append
                }
    
                }

    When I run this directly from $server, $ReportFile gets generated correctly.

    However, when I run:

    Invoke-Command -ComputerName $server  -ScriptBlock {$ScriptBlock}

    , from a different server the report file does not get generated.

    I tried replacing $ScriptBlock with just "get-website," and this works, so I know I can invoke commands remotely.

    I thought this might be a credential issue (on the file share) or double-hop issue, so I tried:

    Invoke-Command -ComputerName $server  -ScriptBlock {$ScriptBlock} -Authentication CredSSP -Credential $Credential

    , but this doesn't generate $ReportFile either.

    Any ideas why $ReportFile isn't getting created when using Invoke-Command to run $ScriptBlock on a remote system?

  • #54270
    Profile photo of Dan Potter
    Dan Potter
    Participant

    Put your get-website function in the scriptblock

  • #54271
    Profile photo of Brandon Lashmet
    Brandon Lashmet
    Participant

    I don't follow. It's already in the scriptblock?

  • #54273
    Profile photo of Rob Simmers
    Rob Simmers
    Participant

    Where does the Get-Website function reside? Is it available on the remote machine? Dan is saying that you need to place the function to be accessible in the scriptblock to ensure it's available in the remote call session:

    $ScriptBlock =  { 
        function Get-WebSite {
            #function code
        }
    
        ForEach($WebSite in $(get-website)) {
            $ReportFile = "\\FileShare\WebLogs\FreeSpaceReport.csv"
            $LogLocation="$($Website.logFile.directory)\w3svc$($website.id)".replace("%SystemDrive%",$env:SystemDrive)
            $LogDrive = (get-item $LogLocation).psdrive.name
            $LogDriveFreeSpace = (get-psdrive $LogDrive).free/1gb
            $LogDriveFreeSpace = "{0:N2}" -f $LogDriveFreeSpace
            $Output = "$server $($WebSite.name) [$LogLocation] $LogDriveFreeSpace"
            $Output | Out-File $ReportFile -append
        }
    }
    
  • #54274
    Profile photo of Daniel Krebs
    Daniel Krebs
    Participant

    I still think you're facing a double-hop problem because CredSSP needs to be properly enabled on the client and server to work and it is unsafe in my opinion because your credentials will be cached on the remote server for quite some time.

    I wouldn't attempt to write to a share from a remote server. My approach as outlined in below example is to gather all the required information from the remote sessions through objects being returned and process them in the session and on the machine that invoked the remote commands.

    $Servers = @( 'Server1', 'Server2' )
    $ReportFile = '\\FileShare\WebLogs\FreeSpaceReport.csv'
    
    $ScriptBlock =  
    {
        Import-Module -Name WebAdministration -Force -ErrorAction Stop
        
        foreach ($Website in $(Get-Website))
        {
            $LogLocation = "$($Website.LogFile.Directory)\w3svc$($Website.Id)".Replace('%SystemDrive%', $env:SystemDrive)
            $LogDrive = (Get-Item $LogLocation).PSDrive.Name
            $LogDriveFreeSpace = '{0:N2}' -f ((Get-PSDrive $LogDrive).Free / 1gb)
    
            [PSCustomObject]@{
                ComputerName = $env:COMPUTERNAME
                SiteName = $Website.Name
                LogLocation = $LogLocation
                LogDriveFreeSpace = $LogDriveFreeSpace
            }
        }
    }
    
    $Results = Invoke-Command -ComputerName $Servers -ScriptBlock $ScriptBlock
    
    $Results | Select-Object -Property ComputerName, SiteName, LogLocation, LogDriveFreeSpace |
        Out-File $ReportFile -Append
    
    • #54328
      Profile photo of Brandon Lashmet
      Brandon Lashmet
      Participant

      Daniel,

      Does this line

      $Results = Invoke-Command -ComputerName $Servers -ScriptBlock $ScriptBlock

      execute the ScriptBlock on all of the servers in $Servers simultaneously, or does it loop through them one at a time?

  • #54276
    Profile photo of Dan Potter
    Dan Potter
    Participant

    =D get-website certainly sounds like a user defined function. Good catch.

  • #54277
    Profile photo of Daniel Krebs
    Daniel Krebs
    Participant

    I don't believe that Get-Website is a user-defined function because the WebAdministration PowerShell module installed on every web server exports this function.

    https://technet.microsoft.com/library/ee807832.aspx

  • #54278
    Profile photo of Dan Potter
    Dan Potter
    Participant

    I was saying it sounded like one, I didn't even think to look it up=D

  • #54325
    Profile photo of Brandon Lashmet
    Brandon Lashmet
    Participant

    Nice catch guys...I did indeed need to define the Get-Website function inside the scriptblock.

    Worked by adding "Import-Module -Name WebAdministration -Force -ErrorAction Stop."

    Thank you!

  • #54376
    Profile photo of Daniel Krebs
    Daniel Krebs
    Participant

    Yes, Invoke-Command will execute the script block concurrently. The default limit is 32 but you can reduce or increase it with the -ThrottleLimit parameter. You can provide thousands of computer names and Invoke-Command will loop through them for you.

    Check out the description of the ThrottleLimit parameter in the official documentation of the command.
    https://technet.microsoft.com/en-us/library/hh849719.aspx

You must be logged in to reply to this topic.