16

According to New EC2 Run Command news article, AWS CLI should support a new sub-command to execute scripts on remote EC2 instances.

However I've checked in aws ec2 help, but I can't find the relevant command.

I've installed aws via apt-get:

$ aws --version
aws-cli/1.14.32 Python/3.5.4 Linux/4.12.7-64 botocore/1.8.36

Which sub-command I should look for and what's the syntax to run, let say ipconfig in PowerShell on the remote EC2 instance?

1
  • 1
    FYI that article is from 2015 so I wouldn't call it a "new" sub-command. I checked out the source code repository (github.com/aws/aws-cli) for the aws-cli, and I couldn't find any mention of it in the docs, examples, release notes, or through briefly looking through the code. I filed an issue (github.com/aws/aws-cli/issues/3126) and will submit an answer when I get feedback.
    – Preston Martin
    Commented Feb 5, 2018 at 16:02

6 Answers 6

15

To run ipconfig from the AWS Systems Manager Run Command:

$ aws ssm send-command --document-name "AWS-RunPowerShellScript" --instance-ids "<your instance id>" --parameters commands=ipconfig

Note: If you've got the error, consider specifying the right --region.

This assumes you have your AWS credentials and CLI configured properly. See Systems Manager Run Command Walkthrough Using the AWS CLI for more information.


Here is the practical shell command example of sending and getting the command output:

cmdid=$(aws ssm send-command --instance-ids "i-ch3ng3th1s" --document-name "AWS-RunPowerShellScript" --parameters commands=ipconfig --query "Command.CommandId" --output text)
aws ssm list-command-invocations --command-id "$cmdid" --details --query "CommandInvocations[*].CommandPlugins[*].Output[]" --output text
1
  • 1
    Can you copy files to an instance too at the same time?
    – red888
    Commented Jun 20, 2020 at 22:10
7

Here is a helper Bash script which uses aws ssm send-command to run the commands:

#/usr/bin/env bash -x
# Script to run PowerShell script on the Windows instance.
instanceId="$1"
cmdId=$(aws ssm send-command --instance-ids "$instanceId" --document-name "AWS-RunPowerShellScript" --query "Command.CommandId" --output text --parameters commands="'${@:2}'")
[ $? -ne 0 ] && { echo "Usage: $0 instance_id command"; exit 1; }
while [ "$(aws ssm list-command-invocations --command-id "$cmdId" --query "CommandInvocations[].Status" --output text)" == "InProgress" ]; do sleep 1; done
aws ssm list-command-invocations --command-id "$cmdId" --details --query "CommandInvocations[*].CommandPlugins[*].Output[]" --output text

Usage:

 ./run_ec2_ps_cmd.sh instance-id command

Example:

$ ./run_ec2_ps_cmd.sh i-xyz hostname
ip-xyz

To execute larger outputs, see: How to avoid output being truncated when running AWS SSM command?

2
  • Actually best example here. More upvotes, please.
    – Sanctus
    Commented Feb 18, 2020 at 18:02
  • Great example: one thing worth mentioning is that the list-commands-invocations does truncate the output, if it is too long
    – Vorsprung
    Commented Mar 5, 2023 at 14:08
3

Yes, you can do this with AWS Systems manager. AWS Systems Manager Run Command allows you to remotely and securely run set of commands on EC2 as well on-premise server. Below are high-level steps to achieve this.

Attach Instance IAM role: The ec2 instance must have IAM role with policy AmazonSSMFullAccess. This role enables the instance to communicate with the Systems Manager API.

Install SSM Agent: The EC2 instance must have SSM agent installed on it. The SSM Agent process the run command requests & configure the instance as per command.

Execute command : Example usage via AWS CLI:

Execute the following command to retrieve the services running on the instance. Replace Instance-ID with ec2 instance id.

aws ssm send-command --document-name "AWS-RunShellScript" --comment "listing services" --instance-ids "Instance-ID" --parameters commands="service --status-all" --region us-west-2 --output text

More detailed information: here

3

Here is something super cool I do with AWS SSM Send-Command!

Using Apache Airflow I create a brand new EC2-Instance using a Cloud Formation Template (or CFT for short) that's just a JSON file with all the configuration values for my EC2-Instance that I want; also note that in this CFT I also have a bootstrap command that copies a Python script from an S3 location to the new EC2-Instance so that I can execute it later on using an SSM Send-Command! I do this using Python3 and the AWS SDK for Python3 called the Boto3 library. Here's part of the command for creating the new CFT Stack that in turn creates my new EC2-Instance:

import boto3

cft = boto3.client("cloudformation", "us-east-1")

response = cft.create_stack(
    StackName='foobarStackName',
    TemplateBody=json.dumps(json_data))

Then I can get the Instance-ID of the new EC2-Instance (required to use SSM Send-Command) using something like this:

response = cft.describe_stacks(
    StackName='foobarStackName',
)

Then I can get the Instance-ID of the current Airflow Worker's server's EC2-Instance by running this command wget -q -O - http://169.254.169.254/latest/meta-data/instance-id through Python:

output = subprocess.Popen(['wget', '-q', '-O', '-', 'http://169.254.169.254/latest/meta-data/instance-id'],
                          stdout=subprocess.PIPE)

# Do some weird stuff to get the value (I'm a Java developer so excuse my bad Python skilz)
instanceId = output.communicate()    
instanceId = str(instanceId).split("'")[1]

NOW!!!! FOR THE GRAND FINALE

I can then execute a script on the new EC2-Instance I created and send that script whatever parameters/arguments I want... including the Instance-ID of the server that sent the SSM Send-Command so that way when my script is done running on the new EC2-Instance it can send another SSM Send-Command back to my Airflow server to tell it that the script is finished. This is at a very high level without details but it's just to demonstrate an idea :)

subprocess.run(shlex.split('sudo aws ssm send-command --document-name "AWS-RunShellScript" --parameters commands=["sudo python3 /home/ec2-user/ec2_file_sensor.py ' + filepath + ' ' + batchIdValue + ' ' + source + ' ' + fileType + ' ' + airflowWorkerInstanceId + '"] --instance-ids ' + ec2ResourceInstanceId + ' --region us-east-1'))

Not sure if this helped anyone but it's a cool and FUN example of doing something with the AWS SSM Send-Command! Albeit, probably a code smell xD

0

If you need to execute more than just one command, because let's say you are investigating an issue, you can start a session:

aws ssm start-session --target i-0123456789abcdef0

You will need to install Session Manager Plugin for that.

0

I've slightly improved the script by @kenorb as it wasn't working for me in 2023. It now accepts the region name as a parameter and properly extracts the CommandID:

#!/usr/bin/env bash

# Script to run a shell script on a Linux instance

while getopts "r:i:" opt; do
  case $opt in
    r) regionId="$OPTARG" ;;
    i) instanceId="$OPTARG" ;;
    *) echo "Usage: $0 -r region_id -i instance_id command"
       exit 1
       ;;
  esac
done

# Remove the flags from the positional parameters
shift $((OPTIND -1))

# Check if regionId and instanceId are provided
if [[ -z "$regionId" || -z "$instanceId" ]]; then
  echo "Usage: $0 -r region_id -i instance_id command"
  exit 1
fi

cmdId=$(aws ssm send-command --document-name "AWS-RunShellScript" --targets "Key=instanceids,Values=$instanceId" --region "$regionId" --output text --parameters commands="'$*'" | grep -Eo '([a-z0-9])+-([a-z0-9])+-([a-z0-9])+-([a-z0-9])+-([a-z0-9])+')
[ $? -ne 0 ] && { echo "Failed to send command"; exit 1; }

while [ "$(aws ssm list-command-invocations --region "$regionId" --command-id "$cmdId" --query "CommandInvocations[].Status" --output text)" == "InProgress" ]; do
  sleep 0.5
done

aws ssm list-command-invocations --region "$regionId" --command-id "$cmdId" --details --query "CommandInvocations[*].CommandPlugins[*].Output[]" --output text 

Results in:

./run_ec2_ps_cmd.sh -i i-INSERTINSTANCEIDHERE -r us-east-1 'echo "Hello World!"'
Hello World!

Don't forget to escape quotes and parameters (dollar signs) in the input, i.e.:

./run_ec2_ps_cmd.sh -i i-INSERTINSTANCEIDHERE -r us-east-1 "netstat -tuln | awk \'{print \$4}\'"

Your Answer

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

Not the answer you're looking for? Browse other questions tagged or ask your own question.