10

I have an ECS task running on Fargate on which I want to run a command in boto3 and get back the output. I can do so in the awscli just fine.

➜ aws ecs execute-command --cluster cluster1 \                                                                                   
    --task abc \
    --container container1 \
    --interactive \
    --command 'echo hi'    

The Session Manager plugin was installed successfully. Use the AWS CLI to start a session.

Starting session with SessionId: ecs-execute-command-0f913e47ae7801aeb
hi

Exiting session with sessionId: ecs-execute-command-0f913e47ae7801aeb.

But I cannot sort out how to get the output for the same in boto3.

ecs = boto3.client("ecs")
ssm = boto3.client("ssm")
exec_resp = ecs.execute_command(
    cluster=self.cluster,
    task=self.task,
    container=self.container,
    interactive=True,
    command="echo hi",
)
s_active = ssm.describe_sessions(
    State="Active",
    Filters=[
        {
            "key": "SessionId",
            "value": exec_resp["session"]["sessionId"],
        },
    ],
)
# Here I get the document for the active session.
doc_active = ssm.get_document(Name=s_active["Sessions"][0]["DocumentName"])
# Now I wait for the session to finish.
s_history = {}
done = False
while not done:
    s_history = ssm.describe_sessions(
        State="History",
        Filters=[
            {
                "key": "SessionId",
                "value": exec_resp["session"]["sessionId"],
            },
        ],
    )
    done = len(s_history["Sessions"]) > 0
doc_history = ssm.get_document(Name=s_history["Sessions"][0]["DocumentName"])

Now the session is terminating and I get another document back, but there still doesn't seem to be output anywhere. Has anybody gotten output from this? How?


For anybody arriving seeking a similar solution, I have created a tool for making this task simple. It is called interloper. This is mostly thanks to the excellent answer by Andrey.

1
  • I don't think you can achieve it via boto3, at least out of the box. The output of the command is transfered via the session manager plugin. Commented Jan 4, 2022 at 22:17

1 Answer 1

13
+50

Ok, basically by reading the ssm session manager plugin source code I came up with the following simplified reimplementation that is capable of just grabbing the command output: (you need to pip install websocket-client construct)

import json
import uuid

import boto3
import construct as c
import websocket

ecs = boto3.client("ecs")
ssm = boto3.client("ssm")
exec_resp = ecs.execute_command(
    cluster=self.cluster,
    task=self.task,
    container=self.container,
    interactive=True,
    command="ls -la /",
)

session = exec_resp['session']
connection = websocket.create_connection(session['streamUrl'])
try:
    init_payload = {
        "MessageSchemaVersion": "1.0",
        "RequestId": str(uuid.uuid4()),
        "TokenValue": session['tokenValue']
    }
    connection.send(json.dumps(init_payload))

    AgentMessageHeader = c.Struct(
        'HeaderLength' / c.Int32ub,
        'MessageType' / c.PaddedString(32, 'ascii'),
    )

    AgentMessagePayload = c.Struct(
        'PayloadLength' / c.Int32ub,
        'Payload' / c.PaddedString(c.this.PayloadLength, 'ascii')
    )

    while True:
        response = connection.recv()

        message = AgentMessageHeader.parse(response)

        if 'channel_closed' in message.MessageType:
            raise Exception('Channel closed before command output was received')

        if 'output_stream_data' in message.MessageType:
            break

finally:
    connection.close()

payload_message = AgentMessagePayload.parse(response[message.HeaderLength:])

print(payload_message.Payload)
Sign up to request clarification or add additional context in comments.

6 Comments

Very nicely done. Thank you so much. Works like a charm.
@theherk nice, glad that it helped.
Hello Andrey, Have you implemented streaming functionality for this? I'm currently working on establishing a real-time stream that captures both input and output on a per-command basis. The goal is to transmit each log entry sequentially and render the interactive terminal session within xterm.js, reflecting the live command execution flow.
I mean this was 3 years ago, that time the code worked. Did you try?
Yes andrey. This code is perfecty working if i want to execute a single command. But i want to continuously stay connected and run other commands just like the terminal. Do you have anything with that we can achive a realtime communication b/w ecs and the web terminal..
My code sends a command, then reads the output then closes the connection. I think you can just do it in a loop: send another command instead closing, rinse and repeat. Bth the op created some tool based on my answer, maybe it does just that: github.com/theherk/interloper

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.