Public/Invoke-ControlCommand.ps1

function Invoke-ControlCommand {
    <#
    .SYNOPSIS
        Will issue a command against a given machine and return the results.
    .DESCRIPTION
        Will issue a command against a given machine and return the results.
    .PARAMETER SessionID
        The GUID identifier for the machine you wish to connect to.
        You can retrieve session info with the 'Get-ControlSessions' commandlet
        SessionIDs can be provided via the pipeline.
        IE - Get-AutomateComputer -ComputerID 5 | Get-ControlSessions | Invoke-ControlCommand -Powershell -Command "Get-Service"
    .PARAMETER Command
        The command you wish to issue to the machine.
    .PARAMETER TimeOut
        The amount of time in milliseconds that a command can execute. The default is 10000 milliseconds.
    .PARAMETER PowerShell
        Issues the command in a powershell session.
    .PARAMETER Group
        Name of session group to use.
    .OUTPUTS
        The output of the Command provided.
    .NOTES
        Version: 2.0
        Author: Chris Taylor
        Modified By: Gavin Stone
        Modified By: Darren White
        Creation Date: 1/20/2016
        Purpose/Change: Initial script development
 
        Update Date: 2019-02-19
        Author: Darren White
        Purpose/Change: Enable Pipeline support. Enable processing using Automate Control Extension. The cached APIKey will be used if present.
    .EXAMPLE
        Get-AutomateComputer -ComputerID 5 | Get-ControlSessions | Invoke-ControlCommand -Powershell -Command "Get-Service"
    .EXAMPLE
        Invoke-ControlCommand -SessionID $SessionID -Command 'hostname'
            Will return the hostname of the machine.
    .EXAMPLE
        Invoke-ControlCommand -SessionID $SessionID -User $User -Password $Password -TimeOut 120000 -Command 'iwr -UseBasicParsing "https://bit.ly/ltposh" | iex; Restart-LTService' -PowerShell
            Will restart the Automate agent on the target machine.
    #>

    [CmdletBinding()]
    param (
        [string]$Server = $Script:ControlServer,
        [System.Management.Automation.PSCredential]$Credentials = $Script:ControlAPICredentials,
        [Parameter(Mandatory=$True,ValueFromPipeline=$True,ValueFromPipelineByPropertyName=$True)]
        [guid[]]$SessionID,
        [string]$Command,
        [int]$TimeOut = 10000,
        [switch]$PowerShell,
        [int]$MaxLength = 5000
    )

    Begin {
        $origin = New-Object -Type DateTime -ArgumentList 1970, 1, 1, 0, 0, 0, 0
        $Server = $Server -replace '/$',''

        # Format command
        $FormattedCommand = @()
        if ($Powershell) {
            $FormattedCommand += '#!ps'
        }
        $FormattedCommand += "#timeout=$TimeOut"
        $FormattedCommand += "#maxlength=$MaxLength"
        $FormattedCommand += $Command
        $FormattedCommand = $FormattedCommand | Out-String
        $SessionEventType = 44
    }

    Process {
        If (!($Server -match 'https?://[a-z0-9][a-z0-9\.\-]*(:[1-9][0-9]*)?$')) {throw "Control Server address is in invalid format."; return}
        ForEach ($GUID IN $SessionID) {
            $Body = ConvertTo-Json @("",@($GUID),$SessionEventType,$FormattedCommand) -Compress
            Write-Verbose $Body

            $RESTRequest = @{
                'URI' = "$Server/App_Extensions/fc234f0e-2e8e-4a1f-b977-ba41b14031f7//ReplicaService.ashx/PageAddEventToSessions"
                'Method' = 'POST'
                'ContentType' = 'application/json'
                'Body' = $Body
            }
            If ($Script:ControlAPIKey) {
                $RESTRequest.Add('Headers',@{'CWAIKToken' = (Get-CWAIKToken)})
            } Else {
                $RESTRequest.Add('Credential',${Script:ControlAPICredentials})
            }
            
            # Issue command
            try {
                $null = Invoke-RestMethod @RESTRequest
            }
            catch {
                Write-Error "$(($_.ErrorDetails | ConvertFrom-Json).message)"
                return
            }

            #Get the timestamp for the Queued command.
            $Body=ConvertTo-Json @("SessionEvent",@("SessionID"),@("LastTime"),"sessionid='$GUID' AND EventType='QueuedCommand'","",10) -Compress
            $RESTRequest = @{
                'URI' = "$Server/App_Extensions/fc234f0e-2e8e-4a1f-b977-ba41b14031f7/ReportService.ashx/GenerateReportForAutomate"
                'Method' = 'POST'
                'ContentType' = 'application/json'
                'Body' = $Body
            }
            If ($Script:ControlAPIKey) {
                $RESTRequest.Add('Headers',@{'CWAIKToken' = (Get-CWAIKToken)})
            } Else {
                $RESTRequest.Add('Credential',${Script:ControlAPICredentials})
            }

            # Get Session
            Write-Verbose $Body
            try {
                $SessionDetails = Invoke-RestMethod @RESTRequest

                $FNames=$SessionDetails.FieldNames
                $SCRecords=($SessionDetails.Items | ForEach-Object {
                    $x=$_
                    $SCEventRecord = [pscustomobject]@{}
                    for($i=0; $i -lt $FNames.Length; $i++){
                        $Null = $SCEventRecord | Add-Member -NotePropertyName $FNames[$i] -NotePropertyValue $x[$i]
                    }
                    $SCEventRecord
                })
                $EventDate=(Get-Date ($SCRecords | Select-Object -Expand LastTime) -UFormat "%Y-%m-%d %T")
            }
            catch {
                Write-Error $($_.Exception.Message)
                return
            }

            # Look for results of command
            $Body=ConvertTo-Json @("SessionConnectionEvent",@(),@("SessionID","Time","Data"),"SessionID='$GUID' AND EventType='RanCommand' AND Time>='$EventDate'","",100) -Compress
            $RESTRequest = @{
                'URI' = "$Server/App_Extensions/fc234f0e-2e8e-4a1f-b977-ba41b14031f7/ReportService.ashx/GenerateReportForAutomate"
                'Method' = 'POST'
                'ContentType' = 'application/json'
                'Body' = $Body
            }

            If ($Script:ControlAPIKey) {
                $RESTRequest.Add('Headers',@{'CWAIKToken' = (Get-CWAIKToken)})
            } Else {
                $RESTRequest.Add('Credential',${Script:ControlAPICredentials})
            }

            $Looking = $True
            $TimeOutDateTime = (Get-Date).AddMilliseconds($TimeOut)
            while ($Looking) {
                Start-Sleep -Seconds 1
                try {
                    $SessionEvents = Invoke-RestMethod @RESTRequest
                }
                catch {
                    Write-Error $($_.Exception.Message)
                    return
                }

                $FNames=$SessionEvents.FieldNames
                $Events = ($SessionEvents.Items | ForEach-Object {$x=$_; $SCEventRecord = [pscustomobject]@{}; for($i=0; $i -lt $FNames.Length; $i++){$Null = $SCEventRecord | Add-Member -NotePropertyName $FNames[$i] -NotePropertyValue $x[$i]}; $SCEventRecord})
                foreach ($Event in $Events) {
                    if ($Event.Time -ge $EventDate) {
                        $Looking = $False
                        $Output = $Event.Data -split '[\r\n]' | Where-Object {$_}
                        if(!$PowerShell){
                            $Output = $Output | Select-Object -skip 1
                        }
                        return $Output 
                    }
                }

                if ($Looking -and $(Get-Date) -gt $TimeOutDateTime.AddSeconds(1)) {
                    $Looking = $False
                    $Output = "Command timed out when sent to Agent"
                }
            }
        }
    }

    End {
        
    }
}