AWS SDK v2 and SSM session

407 views
Skip to first unread message

Xabi Martí

unread,
Jun 14, 2023, 8:35:45 AM6/14/23
to golang-nuts
Hi there, and thanks for having me here, this is my first post and I expect is not going to be the last.

The question is that I've a problem with AWS SDK v2, I'm trying to run a session with a long-running command with SSM Document "AWS-StartNonInteractiveCommand" and get the result and leave it running. Basically is a proxy with port forwarding to an AWS MQ console, like this

ssmSocatCommand := fmt.Sprintf(`
socat -v TCP-LISTEN:0,fork,reuseaddr TCP:%s.mq.us-east-1.amazonaws.com:443 &
SOCAT_PID="$!"
SOCAT_PORT=$(netstat -putona | grep $SOCAT_PID | awk '{print $4}' | cut -d':' -f2)
echo "SOCAT PID: $SOCAT_PID"
echo "SOCAT PORT: $SOCAT_PORT"
`, ID)

startSessionInput := &ssm.StartSessionInput{
DocumentName: aws.String("AWS-StartNonInteractiveCommand"),
Target:       aws.String(bastionID),
Parameters:   map[string][]string{"command": {ssmSocatCommand}},
}

responseStartSession, err := ssmClient.StartSession(ctx, startSessionInput)
if err != nil {
return
}

sessionID := responseStartSession.SessionId
fmt.Println(*sessionID)

filterSessionInput := []ssmTypes.SessionFilter{
{
Key:   ssmTypes.SessionFilterKeySessionId,
Value: sessionID,
},
}

describeSessionsInput := &ssm.DescribeSessionsInput{
State:   ssmTypes.SessionStateActive,
Filters: filterSessionInput,
}

responseDescribeSessions, err := ssmClient.DescribeSessions(ctx, describeSessionsInput)
if err != nil {
log.Fatal(err)
}

if len(responseDescribeSessions.Sessions) > 0 {
session := responseDescribeSessions.Sessions[0]

fmt.Printf("Details: %v\n", *session.Details)
fmt.Printf("DocumentName: %v\n", *session.DocumentName)
fmt.Printf("EndDate: %v\n", session.EndDate)
fmt.Printf("MaxSessionDuration: %v\n", session.MaxSessionDuration)
fmt.Printf("OutputUrl: %v\n", session.OutputUrl)
fmt.Printf("Owner: %v\n", *session.Owner)
fmt.Printf("Reason: %v\n", session.Reason)
fmt.Printf("SessionId: %v\n", *session.SessionId)
fmt.Printf("StartDate: %v\n", session.StartDate)
fmt.Printf("Status: %v\n", session.Status)
fmt.Printf("Target: %v\n", *session.Target)
}
}


The problem is that it seems that with `Start-Session` I'm not able to get the output, and I've some questions:

  • Do I have to get the output with the `StreamUrl`, that's a web socket and from the documentation:

    // A URL back to SSM Agent on the managed node that the Session Manager client
    // uses to send commands and receive output from the node. Format:
    // wss://ssmmessages.region.amazonaws.com/v1/data-channel/session-id?stream=(input|output)
  • Do you know any go package that already implement that?
  • I see that several packages uses https://github.com/aws/session-manager-plugin directly, is that the easiest way?
  • I think that if I use `AWS-RunShellScript` it needs to wait to the command for completion, but I don't want to wait, as it is a long-running command (like 60 minutes or so)
Thanks in advance

Pranav Raja

unread,
Jun 25, 2023, 7:00:42 PM6/25/23
to golang-nuts
> I see that several packages uses https://github.com/aws/session-manager-plugin directly, is that the easiest way?
Yes, this is in fact what the official AWS CLI does (in Python - code here).

I wrote a Go version of this not too long ago on a personal project, here's a snippet of the relevant code (you may need to modify this further for AWS SDK v2):

res, err := client.StartSession(ctx, startSessionInput)
if err != nil {
return envs, err
}
var pluginParams struct {
Target       string
DocumentName string
Parameters   map[string]string
Reason       string
}
pluginParams.Target = *startSessionInput.Target
pluginParams.DocumentName = *startSessionInput.DocumentName
// Note: the structure of this map is a map[string]string,
// which is why I can't just json.Marshal the `startSessionInput` directly.
pluginParams.Parameters = map[string]string{
"command":            ssmSocatCommand,
}
pluginParams.Reason = *startSessionInput.Reason
pluginParamsArg, _ := json.Marshal(pluginParams)
sessionResponse, _ := json.Marshal(res)

// See the AWS CLI for reference on the parameter order to pass to session-manager-plugin
// https://github.com/aws/aws-cli/blob/45b0063b2d0b245b17a57fd9eebd9fcc87c4426a/awscli/customizations/sessionmanager.py#L83
cmd := exec.Command(
"session-manager-plugin",
string(sessionResponse),
region,
"StartSession",
os.Getenv("AWS_PROFILE"),
string(pluginParamsArg),
)


After connecting to the cmd.StdoutPipe() you can Start() the command and monitor its output.

Xabi Martí

unread,
Jun 30, 2023, 5:58:19 AM6/30/23
to golang-nuts
Thanks for answer, finally I ended up using the plugin directly like in your code
Reply all
Reply to author
Forward
0 new messages