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.
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)