Skip to content

Instantly share code, notes, and snippets.

@javipolo
Last active June 9, 2022 00:26
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save javipolo/28147cbb5737a082ede27ef7e28ff90a to your computer and use it in GitHub Desktop.
Save javipolo/28147cbb5737a082ede27ef7e28ff90a to your computer and use it in GitHub Desktop.
Connect to ec2 instances using either InstanceId, Name tag or Ip address, using multiple ssh keys as identities
#!/bin/bash
# Connect to ec2 instances using either InstanceId, Name tag or Ip address
# Usage:
# - ec2ssh i-123124123123
# - ec2ssh [-n 2] name_tag
# - ec2ssh IP
username=core
keypath=~/.ssh/aws
ssh_opts=""
# Use awless or awscli (auto tries awless, and if not, awscli)
backend=auto
ssh_strict=false
# Route through VPN
add_route=false
route='$ip dev tun0 scope link'
usage(){
cat << EOF
Usage:
ec2ssh [options] host [command]
Options:
-l List only, but do not run ssh
-b backend Use backend to query aws [awscli or awless] (Default: $backend)
-r region AWS region
-u username username (Default: $username)
-h hostnum When using a pattern, connect to this number of host on the list
-s true/false If true, use ssh strict hostkey mode (Default: $ssh_strict)
-d debug. Just echo the ssh command
Examples:
ec2ssh i-123124123123
ec2ssh -n 2 -s false name_tag
ec2ssh -u ubuntu i-123412332 ls /
EOF
exit 1
}
add_route(){
export ip=$1
local ip_route="$(echo ${route} | envsubst) "
ip route | grep -qx "$ip_route" || { echo "Adding route to $ip" >&2; sudo ip route add $ip_route; }
}
backend_auto(){
command -v awless > /dev/null && echo -n awless && return
command -v aws > /dev/null && echo -n awscli && return
echo "ERROR. No awless or aws commands found in path. Aborting"
exit 1
}
while getopts "ld:p:r:u:n:s:" o; do
case "${o}" in
l) listonly=true ;;
d) debug="echo" ;;
b) backend=${OPTARG} ;;
p) profile=${OPTARG} ;;
r) region=${OPTARG} ;;
u) username=${OPTARG} ;;
n) hostnum=$((${OPTARG}-1)) ;;
s) ssh_strict=${OPTARG} ;;
*) usage ;;
esac
done
shift $((OPTIND-1))
host=$1; shift
# Resolve auto backend
[ "$backend" == "auto" ] && backend=$(backend_auto)
# Generic functions
get_running_instances(){ ${backend}_get_running_instances $@; }
filter_by(){ ${backend}_filter_by $@; }
get_by_id(){ ${backend}_get_by_id $@; }
get_by_publicip(){ ${backend}_get_by_publicip $@; }
get_by_privateip(){ ${backend}_get_by_privateip $@; }
get_instance_id_by_name(){ ${backend}_get_instance_id_by_name $@; }
list_instances_by_name(){ echo "Instance list"; ${backend}_list_instances_by_name $@ | cat -n; echo ""; }
# awscli stuff
awscli_args(){
args=""
[ "$profile" ] && args="$args --profile $profile"
[ "$region" ] && args="$args --region $region"
echo -n "$args"
}
awscli="aws $(awscli_args)"
awscli_get_running_instances(){
$awscli ec2 describe-instances | jq -r '.Reservations[].Instances[] | select(.State.Name=="running")'
}
awscli_filter_by(){
field=$1
value=$2
echo "$instances" | jq -r 'select(.'$field'=="'$value'") | .PublicIpAddress, .KeyName, (.Tags[] | select(.Key=="Name") | .Value), .InstanceId' | xargs
}
awscli_get_by_id(){
awscli_filter_by InstanceId $1
}
awscli_get_by_publicip(){
awscli_filter_by PublicIpAddress $1
}
awscli_get_by_publicip(){
awscli_filter_by PrivateIpAddress $1
}
awscli_list_instances_by_name(){
name=$1
echo "$instances" | jq -r 'select((.Tags[]|select(.Key=="Name")|.Value) | contains("'$name'")) | "\(.InstanceId) \((.Tags[] | select(.Key=="Name") | .Value)) \(.PublicIpAddress)"' 2>/dev/null
}
awscli_get_instance_id_by_name(){
name=$1
num=${2:-0}
echo "$instances" | jq 'select((.Tags[]|select(.Key=="Name")|.Value) | contains("'$name'"))' 2>/dev/null | jq -s . | jq -r .[$num].InstanceId
}
# awless stuff
awless_args(){
args="--format=json"
[ "$profile" ] && args="$args -p $profile"
[ "$region" ] && args="$args -r $region"
echo -n "$args"
}
awless="awless $(awless_args)"
awless_get_running_instances(){
$awless list instances | jq '.[] | select(.State=="running")'
}
awless_filter_by(){
field=$1
value=$2
echo "$instances" | jq -r 'select(.'$field'=="'$value'") | .PublicIP, .KeyPair, .Name, .ID' | xargs
}
awless_get_by_id(){
awless_filter_by ID $1
}
awless_get_by_publicip(){
awless_filter_by PublicIP $1
}
awless_get_by_privateip(){
awless_filter_by PrivateIP $1
}
awless_list_instances_by_name(){
name=$1
echo "$instances" | jq -r 'select(.[] | contains("'$name'")) | "\(.ID) \(.Name) \(.PublicIP)"' 2>/dev/null
}
awless_get_instance_id_by_name(){
name=$1
num=${2:-0}
echo "$instances" | jq 'select(.[] | contains("'$name'"))' 2>/dev/null | jq -s . | jq -r .[$num].ID
}
# Common code
instances=$(get_running_instances)
# This is a instance ID
if echo $host | grep -e "^i-" -q; then
read -r i_ip i_key i_name i_id <<<$(get_by_id $host)
# Or an IP address
elif echo $host | grep -P "^([0-9]+\.){3}[0-9]+" -q; then
read -r i_ip i_key i_name i_id <<<$(get_by_publicip $host)
[ "$i_ip" ] || read -r i_ip i_key i_name i_id <<<$(get_by_privateip $host)
# or a DNS name that resolves
elif [ "$(dig +short $host)" ]; then
hostIP=$(dig +short $host)
read -r i_ip i_key i_name i_id <<<$(get_by_publicip $hostIP)
# Everything else should be an instance name
elif [ "$listonly" == "true" ]; then
list_instances_by_name $host
exit 0
else
desired_num_instance_id=$(get_instance_id_by_name $host $hostnum)
read -r i_ip i_key i_name i_id <<<$(get_by_id $desired_num_instance_id)
fi
# Disable strict host key checking if needed
[ "$ssh_strict" == "false" ] && ssh_opts="$ssh_opts -o StrictHostKeyChecking=false"
if [ "$i_ip" ]; then
echo -e "ec2ssh to $i_id $i_name $i_ip\n" >&2
[ "$listonly" == "true" ] && exit 0
[ "$add_route" == "true" ] && add_route $i_ip
$debug ssh $ssh_opts -i $keypath/$i_key $username@$i_ip "$@"
else
echo "ERROR. Host '$host' not found"
fi
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment