Today I needed to quickly deploy a piece of software to a few dozen computers. Rather than hassle with creating a SCCM application and deploying it I decided to just script it. I needed a way to take the contents of a folder with a PowerShell script inside, copy the folder to the remote computer and then execute the script afterwards. This is what I came up with and after a few iterations, it ended up working great.

<#
.DESCRIPTION
    This script allows the user to copy a file or folder to a remote computer and execute a Powershell
    script afterwards.  After the script has executed, it then cleans up after itself.  It includes
    validation such as checking to ensure the computer is online and if PS remoting is enabled on the
    remote computer.
.EXAMPLE

.EXAMPLE

.PARAMETER FolderPath
    Any folders (on the local computer) that need copied to the remote computer prior to execution
.PARAMETER ScriptPath
    The Powershell script path (on the local computer) that needs executed on the remote computer
.PARAMETER RemoteDrive
    The remote drive letter the script will be executed on and the folder will be copied to
.PARAMETER Computername
    The remote computer to execute files on.
#>
[CmdletBinding()]
param (
    [Parameter(Mandatory = $True,
               ValueFromPipeline = $True,
               ValueFromPipelineByPropertyName = $True)]
    [string]$Computername,
    [Parameter(Mandatory = $True,
               ValueFromPipeline = $False,
               ValueFromPipelineByPropertyName = $False)]
    [string]$FolderPath,
    [Parameter(Mandatory = $True,
               ValueFromPipeline = $False,
               ValueFromPipelineByPropertyName = $False)]
    [string]$ScriptPath,
    [Parameter(Mandatory = $False,
               ValueFromPipeline = $False,
               ValueFromPipelineByPropertyName = $False)]
    [string]$RemoteDrive = 'C'
)

begin {
    ## http://www.leeholmes.com/blog/2009/11/20/testing-for-powershell-remoting-test-psremoting/
    function Test-PsRemoting {
        param (
            [Parameter(Mandatory = $true)]
            $computername
        )

        try {
            $errorActionPreference = "Stop"
            $result = Invoke-Command -ComputerName $computername { 1 }
        } catch {
            Write-Verbose $_
            return $false
        }

        ## I've never seen this happen, but if you want to be
        ## thorough..
        if ($result -ne 1) {
            Write-Verbose "Remoting to $computerName returned an unexpected result."
            return $false
        }
        $true
    }

    Write-Verbose "Validating prereqs for remote script execution..."
    if (!(Test-Path $FolderPath)) {
        throw 'Folder path does not exist'
    } elseif (!(Test-Path $ScriptPath)) {
        throw 'Script path does not exist'
    } elseif ((Get-ItemProperty -Path $ScriptPath).Extension -ne '.ps1') {
        throw 'Script specified is not a Powershell script'
    } elseif (!(Test-Connection -Computername $Computername -Quiet -Count 1)) {
        throw 'Computer is not reachable'
    } elseif (!(Test-PsRemoting $Computername)) {
        throw 'Remoting is not enabled on remote computer'
    }
    $ScriptName = $ScriptPath | Split-Path -Leaf
    $RemoteFolderPath = $FolderPath | Split-Path -Leaf
    $RemoteScriptPath = "$RemoteDrive<code>:$RemoteFolderPath$ScriptName"
}

process {
    Write-Verbose "Copying the folder $FolderPath to the remote computer $ComputerName..."
    Copy-Item $FolderPath -Recurse "\\$Computername\$RemoteDrive</code>$" -Force
    Write-Verbose "Copying the script $ScriptName to the remote computer $ComputerName..."
    Copy-Item $ScriptPath "\\$Computername\$RemoteDrive<code>$$RemoteFolderPath" -Force
    Write-Verbose "Executing $RemoteDrive</code>:$RemoteFolderPath$ScriptName on the remote computer $ComputerName..."
    ## 05/19/2014 - EDIT - Some remote PCs did not have the powershell.exe association so forcing here
        Invoke-Command -ComputerName $Computername -ScriptBlock { Start-Process powershell.exe -ArgumentList $using:RemoteScriptPath -Wait }
}

end {
    Write-Verbose "Cleaning up the copied folder and script from remote computer $Computername..."
    Remove-Item "\\$ComputerName\$RemoteDrive`$\$RemoteFolderPath" -Recurse -Force
}

Join the Jar Tippers on Patreon

It takes a lot of time to write detailed blog posts like this one. In a single-income family, this blog is one way I depend on to keep the lights on. I'd be eternally grateful if you could become a Patreon patron today!

Become a Patron!