Author Posts

June 2, 2018 at 8:28 am

Hi,

I have a code that zips and copy files from one server to rest of the servers. It reads server names from txt file. If I put only 1 server in txt file all the files are copied correctly. But, if put more then 1 servers in txt file, files are copied on all other servers expect the first one. On first one only 1 or 2 folders are copied.

I am first ziping-> copy -> unzip. Somehow connection seems to drop. See below code :-

function global:CopyMedia ()
{
    $StopWatch = [System.Diagnostics.Stopwatch]::StartNew()
    $StartTime = $StopWatch.Elapsed
    Write-Host "Starting time: $StartTime"
    Write-Host "Reading Config File....."
    $ConfigFile = Split-Path -Path $PSCommandPath 
    $ConfigFile = $ConfigFile + "\" + "Config.ps1"
    ."$ConfigFile"

    $RunspaceError = $null

    $pool = [RunspaceFactory]::CreateRunspacePool(1, $ParallelServerThreshold)
    $pool.ApartmentState = "MTA"
    $pool.Open()
    $runspaces = @()

    $scriptblock = 
    {
        Param (
        [PSCredential] $Cred,
        [string] $UserName,
        [string] $Password,
        [string] $ServerName,
        [string] $Drive,
        [string] $SourceDrive,
        [string] $SourceFolder,
        [string] $File
        )

        $UnzipFile = $Drive + "\AHSInstall\" + $File
        $UnZipFolder = $Drive + "\AHSInstall\"

        Invoke-Command -ComputerName $ServerName -Credential $cred -ScriptBlock {
            NET USE $using:SourceDrive /u:$using:UserName $using:Password
            Robocopy $using:SourceFolder ($using:Drive + "\AHSInstall\") $using:File /J
            NET USE $using:SourceDrive /D
            
            Expand-Archive -Path $using:UnzipFile -DestinationPath $using:UnZipFolder -Force
            Remove-Item –path $using:UnzipFile
        }
    }

    try 
    {
        $ToCopyFile = $ToCopyFolder + '.zip'
        $SourceFolder = "\\" + "$env:computername.$env:userdnsdomain" + "\" + $ToCopyFolder.Replace(":", "$")
        $SourceFolder = (Split-Path -Path $SourceFolder) + "\"
        $SourceDrive = Split-Path -Path $SourceFolder

        Write-Host "Compressing folders for copy....."
        Compress-Archive -Path $ToCopyFolder -CompressionLevel NoCompression -DestinationPath $ToCopyFile -Force
        $File = Split-Path -Path $ToCopyFile -Leaf -Resolve

        Write-Host "Reading Servers from text file..."
        foreach ($Server in $Servers)
        {
            $ServerName = $Server.Trim()

            if($ServerName -eq $env:computername -Or $ServerName -eq ("$env:computername.$env:userdnsdomain")) 
            { 
                Write-Host "No need to copy as running script from same server: $ServerName" -ForegroundColor Green
                Continue
            }

            If($PreferredDrive.Trim().Length -eq 2)
             {
                Write-Host "Checking Freespace on drive $PreferredDrive on server $ServerName....."
                $Disk = Get-WMIObject Win32_Logicaldisk -ComputerName $ServerName -Credential $Cred -Filter "DeviceID='$PreferredDrive'" | Select-Object DeviceID, Freespace
                $Drive = $PreferredDrive.Trim()
                if ($Disk -eq $null) 
                 {
                    Write-Host "`nDrive $Drive does not exists on server $ServerName.....`n" -ForegroundColor Yellow
                    Continue
                 }
             }
            Else
             {
                Write-Host "Finding drive having maximum Freespace on server $ServerName....."
                $Disk = Get-WMIObject Win32_Logicaldisk -ComputerName $ServerName -Credential $Cred | Select DeviceID, Freespace | sort Freespace -Descending | select -First 1
                $Drive = $Disk.DeviceID    
             }
        
            $Freespace = $Disk.Freespace/1024/1024/1024
            if($Freespace -le $MinimumDriveSpaceRequired)
             { 
                Write-Host "`nNot enough Freespace on drive $Drive on server $ServerName`n" -ForegroundColor Yellow
                Continue
             }

            Write-Host "Starting thread for server: $ServerName. Which will do copy, unzip & delete....."
            $runspace = [PowerShell]::Create()
            $null = $runspace.AddScript($scriptblock)
            $null = $runspace.AddArgument($Cred)
            $null = $runspace.AddArgument($WindowsDomainUsername)
            $null = $runspace.AddArgument($WindowsDomainUsernamePassword)
            $null = $runspace.AddArgument($ServerName)
            $null = $runspace.AddArgument($Drive)
            $null = $runspace.AddArgument($SourceDrive)
            $null = $runspace.AddArgument($SourceFolder)
            $null = $runspace.AddArgument($File)
            $runspace.RunspacePool = $pool
            $runspaces += [PSCustomObject]@{ Pipe = $runspace; Status = $runspace.BeginInvoke() }
        }
        
        if($runspaces -ne $null)
         {
            Write-Host "Copy in progress. Please wait....."
            while ($runspaces.Status.IsCompleted -notcontains $true) {}

            Write-Host "Checking for any errors in the threads and disposing them....."
            $i=0
            foreach ($runspace in $runspaces) 
             {
                $RunspaceError += $runspaces[$i].Pipe.Streams.Error
                $runspace.Pipe.Dispose()
                $i++
             }
            if($RunspaceError -cne $null)
             {
                Write-Host "`nFollowing error occured in threads: `n$RunspaceError" -ForegroundColor Red
             }
            else
            {
                Write-Host "No erros have been found. All the threads completed successfully....." -ForegroundColor Green
            }   
         }

        Write-Host "Deleting zipped copy....."
        Remove-Item –path $ToCopyFile
        
        Write-Host "Media Copy Completed....."
        Write-Host "`nMedia has been copied on below servers:"
        Write-Output $Servers
        Write-Host "`n"
    }
    catch
    {
        Write-Host "Following error occured: `n$($PSItem.ToString())" -ForegroundColor Cyan
    }
    finally
    {
        $pool.Close() 
        $pool.Dispose()
    }

   $EndTime = $StopWatch.Elapsed
   Write-Host "End time: $EndTime"
   $TimeTaken = $EndTime - $StartTime
   Write-Host "Time taken to copy media on all servers: $TimeTaken"
   
   Write-Host "***********************************"
   Read-Host -Prompt "Check for any errors in the above script. Hit enter to exit copy media script"
}

June 3, 2018 at 12:58 pm

Sorry if you expected something else. I don't like to digg into your big chunk of code. You might be better of splitting off your giant function in smaller pieces. If you have "simpler" functions you could stich them together with a controller script. They would be easier to maintain and to debug and probalby even better reusable.

June 4, 2018 at 10:44 am

Hi Olaf,

Thanks for taking time and going through this. I am actually using Runspaces as per post (https://blog.netnerds.net/2016/12/runspaces-simplified/) it has 6 parts so it might look big. Actual working code is just inside scriptblock. I also have one controller (master) script which call all of these scripts.

$scriptblock = 
    {
        Param (
        [PSCredential] $Cred,
        [string] $UserName,
        [string] $Password,
        [string] $ServerName,
        [string] $Drive,
        [string] $SourceDrive,
        [string] $SourceFolder,
        [string] $File
        )

        $UnzipFile = $Drive + "\AHSInstall\" + $File
        $UnZipFolder = $Drive + "\AHSInstall\"

        Invoke-Command -ComputerName $ServerName -Credential $cred -ScriptBlock {
            NET USE $using:SourceDrive /u:$using:UserName $using:Password
            Robocopy $using:SourceFolder ($using:Drive + "\AHSInstall\") $using:File /J
            NET USE $using:SourceDrive /D
            
            Expand-Archive -Path $using:UnzipFile -DestinationPath $using:UnZipFolder -Force
            Remove-Item –path $using:UnzipFile
        }
    }