I want to connect to my ec2 instance using aws ssm sdk for rust. What is the best way to create an interactive terminal session? Here is the following code I am using to create an ssm session:
pub async fn create_ssh_session(&self, instance_id: &str) -> Result<()> {
let output = self
.client
.start_session()
.document_name("SSM-SessionManagerRunShell")
.target(instance_id)
.send()
.await?;
let wss_url = output.stream_url().unwrap();
let (mut ws_stream, response) = tokio_tungstenite::connect_async(Url::parse(wss_url)?).await?;
// Print response to confirm successful connection
println!("Connected: {}", response.status());
let output = self
.client
.terminate_session()
.session_id(output.session_id().unwrap())
.send()
.await?;
// println!("{:#?}", output);
Ok(())
}
I have tried to connect using tokio tungstenite to connect to the WSS url but I am not sure if this is the best way.
EDIT: when I try to connect, it gives me status code 101 (switching protocols) what does this mean and after starting session with ssm I receive wss_url and the token. According to the documentation, it says the token is used to authenticate connection with managed node. I want to know how I can do so when creating the stream.
Instead of communicating directly with the ssm agent on the instance, I decided it would be simpler to interact with the session-manager-plugin
as suggested by @john. There is no solid reference that I could find in regards to using session-manager-plugin from the command line and I referred to this question here.
here is the basic workflow on how one can spawn a session using the aws sdk for rust with session-manager-plugin
:
pub async fn create_ssh_session(&self, instance_id: &str) -> Result<()> {
// start session get session_id, token_value and stream_url
let output = self
.client
.start_session()
.document_name("SSM-SessionManagerRunShell")
.target(instance_id)
.send()
.await?;
// create ssm plugin json message
let response = ResponseJson {
SessionId: output.session_id().unwrap().to_string(),
TokenValue: output.token_value().unwrap().to_string(), // Assuming `token` is defined elsewhere
StreamUrl: output.stream_url().unwrap().to_string(),
};
// commenting code for reference
// let template =r#"{"Target" : "{instance_id}","DocumentName": "AWS-StartPortForwardingSession","Parameters" : {"portNumber": [22],"localPortNumber": [3232] }}"#;
// let ssm_plugin_document = json!(template.replace("{instance_id}", instance_id.into()));
// let plugin_string = serde_json::to_string(&ssm_plugin_document)?;
let response_string = serde_json::to_string(&response)?;
let mut session_manager_plugin = Command::new("session-manager-plugin");
let run_command_output = session_manager_plugin
.args([
response_string,
"ap-southeast-2".into(),
"StartSession".into(),
"https://ssm.ap-southeast-2.amazonaws.com/".into(),
])
.spawn()?;
let result = run_command_output.wait_with_output()?;
let output = self
.client
.terminate_session()
.session_id(output.session_id().unwrap())
.send()
.await?;
Ok(())
}
I am simply spawning session-manager-plugin
as a process with the right arguments using the SSM-SessionManagerRunShell
document.