Public/Invoke-DockerContainerCommand.ps1

function Invoke-DockerContainerCommand
{
    <#
        .SYNOPSIS
            Invokes a command inside a Docker container.
        .DESCRIPTION
            Invokes a command inside a Docker container.
        .PARAMETER ContainerId
            An array of strings containing the full id(s) (64 characters) of the container(s) where the command will be run.
        .PARAMETER Container
            An array of pscustomobjects containing the container(s) where the command will be run.
        .PARAMETER ComputerName
            An array of string containing the computer(s) where the containers are hosted.
        .PARAMETER Command
            An array of string containing the command(s) to run in the container.
        .PARAMETER Expression
            A string containing the expression to run in the container.
        .PARAMETER ScriptBlock
            An script block describing the command(s) to run in the container.
        .PARAMETER FilePath
            A string containing the path of a local powershell script to run in the container.
        .PARAMETER ArgumentList
            An array of object specifying the arguments - local variables or values - to pass to the command, script or script block.
            The param keyword lists the local variables that are used in the command. ArgumentList supplies the values of the variables, in the order that they're listed.
        .PARAMETER RunAsAdministrator
            A switch sepcifying wheter or not this cmdlet invokes a command as an Administrator.
        .PARAMETER AsJob
            A switch specifying whether or not to run the command as a background job in the container.
        .PARAMETER JobName
            A string the name of the job if the command is run as a background job in the container. By default, jobs are named Job<n>, where <n> is an ordinal number.
            If you use the JobName parameter in a command, the command is run as a job, and Invoke-DockerContainerCommand returns a job object, even if you do not include AsJob in the command.
        .PARAMETER Credential
            A PSCredential used to connect to the host.
        .PARAMETER Authentication
            An AuthenticationMechanism that will be used to authenticate the user's credentials.
        .PARAMETER AuthenticationOn
            An array of string specifying whether to use Credential and Authentication on the host, the container or both.
        .PARAMETER Force
            A switch specifying whether or not to force the action.
        .INPUTS
            System.Management.Automation.PSCustomObject
            You can pipe a value for the containers to this cmdlet.
        .OUTPUTS
            System.Management.Automation.PSRemotingJob, or the output of the invoked command
            This cmdlet returns a job object, if you use the AsJob parameter. Otherwise, it returns the output of the invoked command, which is the value of the ScriptBlock parameter.
        .EXAMPLE
            Invoke-DockerContainerCommand -Command "Get-Service -Name MyService" -ContainerId 4f657b6d8066b87455303c591873b0739aa14f589cd56365a46a256f726c6be0 -ComputerName MyHostServer
 
            Description
            -----------
            This example will get the service MyService in the container with id '4f657b6d8066b87455303c591873b0739aa14f589cd56365a46a256f726c6be0' on computer 'MyHostServer'.
        .EXAMPLE
            Get-DockerContainer | Invoke-DockerContainerCommand -ScriptBlock {
                param([string] $ServiceName)
 
                Get-Service -Name $ServiceName | Stop-Service
                [...]
                Get-Service -Name $ServiceName | Start-Service
            } -ArgumentList "MyService"
 
            Description
            -----------
            This example will get all containers hosted on the current computer, stop the service MyService, do some processing and start the service on each of them.
        .EXAMPLE
            Get-ChildItem
                Directory: C:\Script
 
            Mode LastWriteTime Length Name
            ---- ------------- ------ ----
            -a---- 2020-09-02 8:56 AM 4579 BackEndScript.ps1
            -a---- 2020-09-02 11:25 AM 5979 FrontEndScript.ps1
 
            Get-DockerContainer -Name "*BackEnd" -ComputerName MyHostServer -Credential DOMAIN\MyUser -Authentication CredSSP | Invoke-DockerContainerCommand -FilePath C:\Scripts\BackEndScript.ps1 -ArgumentList $Arg0, $Arg1 -RunAsAdministrator -RunAsJob -Credential DOMAIN\MyUser -Authentication CredSSP -AuthenticationOn Host, Docker
 
            Description
            -----------
            This example will get all containers that match "*BackEnd" on computer MyHostServer, using DOMAIN\MyUser with CredSSP on both the host server and the container, and run the script C:\Scripts\BackEndScript.ps1 as administrator and as a job in each container.
        .NOTES
        .LINK
            Invoke-Command
    #>

    [CmdLetBinding(SupportsShouldProcess = $true, ConfirmImpact = 'High')]
    param(
        [Parameter(ParameterSetName = "FromContainerObjectAndCommand", Mandatory = $true, ValueFromPipeline = $true)]
        [Parameter(ParameterSetName = "FromContainerObjectAndScriptBlock", Mandatory = $true, ValueFromPipeline = $true)]
        [Parameter(ParameterSetName = "FromContainerObjectAndFilePath", Mandatory = $true, ValueFromPipeline = $true)]
        [Parameter(ParameterSetName = "FromContainerObjectAndExpression", Mandatory = $true, ValueFromPipeline = $true)]
        [pscustomobject[]] $Container,
        [Parameter(ParameterSetName = "FromContainerIdAndCommand", Mandatory = $true)]
        [Parameter(ParameterSetName = "FromContainerIdAndScriptBlock", Mandatory = $true)]
        [Parameter(ParameterSetName = "FromContainerIdAndFilePath", Mandatory = $true)]
        [Parameter(ParameterSetName = "FromContainerIdAndExpression", Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [ValidateLength(64, 64)]
        [string] $ContainerId,
        [Parameter(ParameterSetName = "FromContainerIdAndCommand", Mandatory = $false)]
        [Parameter(ParameterSetName = "FromContainerIdAndScriptBlock", Mandatory = $false)]
        [Parameter(ParameterSetName = "FromContainerIdAndFilePath", Mandatory = $false)]
        [Parameter(ParameterSetName = "FromContainerIdAndExpression", Mandatory = $false)]
        [Alias("Cn")]
        [string] $ComputerName = $env:COMPUTERNAME,
        [Parameter(ParameterSetName = "FromContainerObjectAndCommand", Mandatory = $true)]
        [Parameter(ParameterSetName = "FromContainerIdAndCommand", Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [string[]] $Command,
        [Parameter(ParameterSetName = "FromContainerObjectAndFilePath", Mandatory = $true)]
        [Parameter(ParameterSetName = "FromContainerIdAndFilePath", Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [ValidateScript( { Test-Path $_ } )]
        [Alias("PSPath")]
        [string] $FilePath,
        [Parameter(ParameterSetName = "FromContainerObjectAndExpression", Mandatory = $true)]
        [Parameter(ParameterSetName = "FromContainerIdAndExpression", Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [string] $Expression,
        [Parameter(ParameterSetName = "FromContainerObjectAndScriptBlock", Mandatory = $true)]
        [Parameter(ParameterSetName = "FromContainerIdAndScriptBlock", Mandatory = $true)]
        [scriptblock] $ScriptBlock,
        [Parameter(ParameterSetName = "FromContainerObjectAndCommand", Mandatory = $false)]
        [Parameter(ParameterSetName = "FromContainerObjectAndScriptBlock", Mandatory = $false)]
        [Parameter(ParameterSetName = "FromContainerObjectAndFilePath", Mandatory = $false)]
        [Parameter(ParameterSetName = "FromContainerIdAndCommand", Mandatory = $false)]
        [Parameter(ParameterSetName = "FromContainerIdAndScriptBlock", Mandatory = $false)]
        [Parameter(ParameterSetName = "FromContainerIdAndFilePath", Mandatory = $false)]
        [Alias("Args")]
        [object[]] $ArgumentList,
        [Parameter(ParameterSetName = "FromContainerObjectAndCommand", Mandatory = $false)]
        [Parameter(ParameterSetName = "FromContainerObjectAndScriptBlock", Mandatory = $false)]
        [Parameter(ParameterSetName = "FromContainerObjectAndFilePath", Mandatory = $false)]
        [Parameter(ParameterSetName = "FromContainerObjectAndExpression", Mandatory = $false)]
        [Parameter(ParameterSetName = "FromContainerIdAndCommand", Mandatory = $false)]
        [Parameter(ParameterSetName = "FromContainerIdAndScriptBlock", Mandatory = $false)]
        [Parameter(ParameterSetName = "FromContainerIdAndFilePath", Mandatory = $false)]
        [Parameter(ParameterSetName = "FromContainerIdAndExpression", Mandatory = $false)]
        [switch] $RunAsAdministrator,
        [Parameter(ParameterSetName = "FromContainerObjectAndCommand", Mandatory = $false)]
        [Parameter(ParameterSetName = "FromContainerObjectAndScriptBlock", Mandatory = $false)]
        [Parameter(ParameterSetName = "FromContainerObjectAndFilePath", Mandatory = $false)]
        [Parameter(ParameterSetName = "FromContainerObjectAndExpression", Mandatory = $false)]
        [Parameter(ParameterSetName = "FromContainerIdAndCommand", Mandatory = $false)]
        [Parameter(ParameterSetName = "FromContainerIdAndScriptBlock", Mandatory = $false)]
        [Parameter(ParameterSetName = "FromContainerIdAndFilePath", Mandatory = $false)]
        [Parameter(ParameterSetName = "FromContainerIdAndExpression", Mandatory = $false)]
        [switch] $AsJob,
        [Parameter(ParameterSetName = "FromContainerObjectAndCommand", Mandatory = $false)]
        [Parameter(ParameterSetName = "FromContainerObjectAndScriptBlock", Mandatory = $false)]
        [Parameter(ParameterSetName = "FromContainerObjectAndFilePath", Mandatory = $false)]
        [Parameter(ParameterSetName = "FromContainerObjectAndExpression", Mandatory = $false)]
        [Parameter(ParameterSetName = "FromContainerIdAndCommand", Mandatory = $false)]
        [Parameter(ParameterSetName = "FromContainerIdAndScriptBlock", Mandatory = $false)]
        [Parameter(ParameterSetName = "FromContainerIdAndFilePath", Mandatory = $false)]
        [Parameter(ParameterSetName = "FromContainerIdAndExpression", Mandatory = $false)]
        [string] $JobName,
        [Parameter(ParameterSetName = "FromContainerObjectAndCommand", Mandatory = $false)]
        [Parameter(ParameterSetName = "FromContainerObjectAndScriptBlock", Mandatory = $false)]
        [Parameter(ParameterSetName = "FromContainerObjectAndFilePath", Mandatory = $false)]
        [Parameter(ParameterSetName = "FromContainerObjectAndExpression", Mandatory = $false)]
        [Parameter(ParameterSetName = "FromContainerIdAndCommand", Mandatory = $false)]
        [Parameter(ParameterSetName = "FromContainerIdAndScriptBlock", Mandatory = $false)]
        [Parameter(ParameterSetName = "FromContainerIdAndFilePath", Mandatory = $false)]
        [Parameter(ParameterSetName = "FromContainerIdAndExpression", Mandatory = $false)]
        [pscredential] $Credential,
        [Parameter(ParameterSetName = "FromContainerObjectAndCommand", Mandatory = $false)]
        [Parameter(ParameterSetName = "FromContainerObjectAndScriptBlock", Mandatory = $false)]
        [Parameter(ParameterSetName = "FromContainerObjectAndFilePath", Mandatory = $false)]
        [Parameter(ParameterSetName = "FromContainerObjectAndExpression", Mandatory = $false)]
        [Parameter(ParameterSetName = "FromContainerIdAndCommand", Mandatory = $false)]
        [Parameter(ParameterSetName = "FromContainerIdAndScriptBlock", Mandatory = $false)]
        [Parameter(ParameterSetName = "FromContainerIdAndFilePath", Mandatory = $false)]
        [Parameter(ParameterSetName = "FromContainerIdAndExpression", Mandatory = $false)]
        [ValidateSet("Basic", "Default", "Credssp", "Digest", "Kerberos", "Negotiate", "NegotiateWithImplicitCredential")]
        [System.Management.Automation.Runspaces.AuthenticationMechanism] $Authentication = "Default",
        [Parameter(ParameterSetName = "FromContainerObjectAndCommand", Mandatory = $false)]
        [Parameter(ParameterSetName = "FromContainerObjectAndScriptBlock", Mandatory = $false)]
        [Parameter(ParameterSetName = "FromContainerObjectAndFilePath", Mandatory = $false)]
        [Parameter(ParameterSetName = "FromContainerObjectAndExpression", Mandatory = $false)]
        [Parameter(ParameterSetName = "FromContainerIdAndCommand", Mandatory = $false)]
        [Parameter(ParameterSetName = "FromContainerIdAndScriptBlock", Mandatory = $false)]
        [Parameter(ParameterSetName = "FromContainerIdAndFilePath", Mandatory = $false)]
        [Parameter(ParameterSetName = "FromContainerIdAndExpression", Mandatory = $false)]
        [ValidateSet("Host", "Container")]
        [string[]] $AuthenticationOn = "Host",
        [Parameter(ParameterSetName = "FromContainerObjectAndCommand", Mandatory = $false)]
        [Parameter(ParameterSetName = "FromContainerObjectAndScriptBlock", Mandatory = $false)]
        [Parameter(ParameterSetName = "FromContainerObjectAndFilePath", Mandatory = $false)]
        [Parameter(ParameterSetName = "FromContainerObjectAndExpression", Mandatory = $false)]
        [Parameter(ParameterSetName = "FromContainerIdAndCommand", Mandatory = $false)]
        [Parameter(ParameterSetName = "FromContainerIdAndScriptBlock", Mandatory = $false)]
        [Parameter(ParameterSetName = "FromContainerIdAndFilePath", Mandatory = $false)]
        [Parameter(ParameterSetName = "FromContainerIdAndExpression", Mandatory = $false)]
        [switch] $Force
    )

    begin
    {
        Write-Debug ($script:LocalizedData.Global.Debug.Entering -f $PSCmdlet.MyInvocation.MyCommand)
        if ($PSCmdlet.ParameterSetName -match "FromContainerId")
        {
            $Container = New-Object -TypeName PSCustomObject @{
                FullId = $ContainerId;
                ComputerName = $ComputerName
            }
            $PSBoundParameters.Remove("ContainerId") | Out-Null
            if ($PSBoundParameters.ComputerName) { $PSBoundParameters.Remove("ComputerName") | Out-Null }
        }
        if ($PSBoundParameters.Force) { $PSBoundParameters.Remove("Force") | Out-Null }
        if ($PSBoundParameters.AuthenticationOn) { $PSBoundParameters.Remove("AuthenticationOn") | Out-Null }
    }
    process
    {
        $Container | ForEach-Object {
            $CurrentContainer = $_
            try 
            {
                $HostParameters = @{
                    ComputerName = $CurrentContainer.ComputerName
                }
                if ($AuthenticationOn -contains "Host")
                {
                    if ($PSBoundParameters.Credential) { $HostParameters.Add("Credential", $Credential) }
                    if ($PSBoundParameters.Authentication) { $HostParameters.Add("Authentication", $Authentication) }
                }

                $ContainerParameters = $PSBoundParameters
                if ($ContainerParameters.Container) { $ContainerParameters.Remove("Container") | Out-Null }
                $ContainerParameters.ContainerId = $CurrentContainer.FullId
                if ($ContainerParameters.FilePath) { $ContainerParameters.FilePath = Get-Content $ContainerParameters.FilePath }
                if (!($AuthenticationOn -contains "Container"))
                {
                    if ($ContainerParameters.Credential) { $ContainerParameters.Remove("Credential") | Out-Null }
                    if ($ContainerParameters.Authentication) { $ContainerParameters.Remove("Authentication") | Out-Null }
                }
                if ($ContainerParameters.WhatIf) { $ContainerParameters.Remove("WhatIf") | Out-Null }
                if ($ContainerParameters.Confirm) { $ContainerParameters.Remove("Confirm") | Out-Null }
                if ($CurrentContainer.Name) { $Target = $CurrentContainer.Name }
                else { $Target = $CurrentContainer.FullId }

                if ($Force -or $PSCmdlet.ShouldProcess($Target))
                {
                    Invoke-Command @HostParameters -ScriptBlock {
                        try
                        {
                            $ContainerParameters = $using:ContainerParameters
                            if ($ContainerParameters.Expression)
                            {
                                $ContainerParameters.Add("Command", "Invoke-Expression `"$($ContainerParameters.Expression)`"")
                                $ContainerParameters.Remove("Expression") | Out-Null
                            }
                            if ($ContainerParameters.FilePath)
                            {
                                $FilePath = (Join-Path $env:TEMP ("{0}.ps1" -f (New-Guid).ToString()))
                                $ContainerParameters.FilePath | Set-Content $FilePath
                                $ContainerParameters.FilePath = $FilePath
                            }
                            if ($ContainerParameters.Command) { $ContainerParameters.Command = [scriptblock]::Create($ContainerParameters.Command -join ";") }
                            if ($ContainerParameters.ScriptBlock) { $ContainerParameters.ScriptBlock = [scriptblock]::Create($ContainerParameters.ScriptBlock) }

                            Invoke-Command @ContainerParameters     
                        }
                        catch
                        {
                            Write-Error $_
                        }
                        finally 
                        {
                            if ($ContainerParameters.FilePath) { Remove-Item $ContainerParameters.FilePath -Force -ErrorAction SilentlyContinue }
                        }
                    }
                }
            }
            catch 
            {
                Write-Error $_    
            }
        }
    }
    end
    {
        Write-Debug ($script:LocalizedData.Global.Debug.Leaving -f $PSCmdlet.MyInvocation.MyCommand)
    }
}