Author Posts

September 19, 2016 at 3:38 pm

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?

September 19, 2016 at 4:12 pm

Put your get-website function in the scriptblock

September 19, 2016 at 6:54 pm

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

September 19, 2016 at 7:10 pm

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
    }
}

September 19, 2016 at 7:17 pm

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

September 19, 2016 at 7:40 pm

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

September 19, 2016 at 7:50 pm

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

September 19, 2016 at 7:55 pm

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

September 20, 2016 at 4:30 pm

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!

September 20, 2016 at 4:57 pm

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?

September 21, 2016 at 7:01 am

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