62

I would like to run some scripts on hosts which are EC2 instances but I don't know how to be sure that the host is really an EC2 instance.

I have made some tests, but this is not sufficient:

  • Test that binary ec2_userdata is available (but this will not always be true)
  • Test availability of "http://169.254.169.254/latest/meta-data" (but will this be always true ? and what is this "magical IP" ?)
6
  • 1
  • It's an APIPA address actually, which is quite odd to use as a reference for a critical service like meta data retrieval. Jan 4, 2013 at 9:52
  • 2
    The IP ranges of EC2s are public (though varying from time to time). If you keep up with a current list you can check the instances IP against that ranges. Jan 4, 2013 at 10:30
  • 2
    Don't rely on 169.254.169.254 if you want EC2 and only EC2 - EC2-alike systems like Eucalyptus also support it. engage.eucalyptus.com/customer/portal/articles/…
    – ceejayoz
    Jul 2, 2013 at 14:21
  • 1
    Do you need the method to work against an attacker who has root on the host, and is trying to spoof you into thinking that it's an EC2 instance for his own malicious purposes? If you do, then it will be much harder.
    – Mike Scott
    Jun 22, 2015 at 19:01

13 Answers 13

50

First, I felt the need to post a new answer because of the following subtle problems with the existing answers, and after receiving a question about my comment on @qwertzguy's answer. Here are the problems with the current answers:

  1. The accepted answer from @MatthieuCerda definitely does not work reliably, at least not on any VPC instances I checked against. (On my instances, I get a VPC name for hostname -d, which is used for internal DNS, not anything with "amazonaws.com" in it.)
  2. The highest-voted answer from @qwertzguy does not work on new m5 or c5 instances, which do not have this file. Amazon neglects to document this behavior change AFAIK, although the doc page on this subject does say "... If /sys/hypervisor/uuid exists ...". I asked AWS support whether this change was intentional, see below †.
  3. The answer from @Jer does not necessarily work everywhere because the instance-data.ec2.internal DNS lookup may not work. On an Ubuntu EC2 VPC instance I just tested on, I see: $ curl http://instance-data.ec2.internal curl: (6) Could not resolve host: instance-data.ec2.internal which would cause code relying on this method to falsely conclude it is not on EC2!
  4. The answer to use dmidecode from @tamale may work, but relies on you a.) having dmidecode available on your instance, and b.) having root or sudo password-less ability from within your code.
  5. The answer to check /sys/devices/virtual/dmi/id/bios_version from @spkane is dangerously misleading! I checked one Ubuntu 14.04 m5 instance, and got a bios_version of 1.0. This file is not documented at all on Amazon's doc, so I would really not rely on it.
  6. The first part of the answer from @Chris-Montanaro to check an unreliable 3rd-party URL and use whois on the result is problematic on several levels. Note the URL suggested in that answer is a 404 page right now! Even if you did find a 3rd-party service that did work, it would be comparatively very slow (compared to checking a file locally) and possibly run into rate-limiting issues or network issues, or possibly your EC2 instance doesn't even have outside network access.
  7. The second suggestion in the answer from @Chris-Montanaro to check http://169.254.169.254/ is a little better, but another commenter notes that other cloud providers make this instance metadata URL available, so you have to be careful to avoid false positives. Also it will still be much slower than a local file, I have seen this check be especially slow (several seconds to return) on heavily loaded instances. Also, you should remember to pass a -m or --max-time argument to curl to avoid it hanging for a very long time, especially on a non-EC2 instance where this address may lead to nowhere and hang (as in @algal's answer).

Also, I don't see that anyone has mentioned Amazon's documented fallback of checking for the (possible) file /sys/devices/virtual/dmi/id/product_uuid.

Who knew that determining whether you are running on EC2 could be so complicated?! OK, now that we have (most) of the problems with listed approaches listed, here is a suggested bash snippet to check whether you are running on EC2. I think this should work generally on almost any Linux instances, Windows instances are an exercise for the reader.

#!/bin/bash

# This first, simple check will work for many older instance types.
if [ -f /sys/hypervisor/uuid ]; then
  # File should be readable by non-root users.
  if [ `head -c 3 /sys/hypervisor/uuid` == "ec2" ]; then
    echo yes
  else
    echo no
  fi

# This check will work on newer m5/c5 instances, but only if you have root!
elif [ -r /sys/devices/virtual/dmi/id/product_uuid ]; then
  # If the file exists AND is readable by us, we can rely on it.
  if [ `head -c 3 /sys/devices/virtual/dmi/id/product_uuid` == "EC2" ]; then
    echo yes
  else
    echo no
  fi

else
  # Fallback check of http://169.254.169.254/. If we wanted to be REALLY
  # authoritative, we could follow Amazon's suggestions for cryptographically
  # verifying their signature, see here:
  #    https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/instance-identity-documents.html
  # but this is almost certainly overkill for this purpose (and the above
  # checks of "EC2" prefixes have a higher false positive potential, anyway).
  if $(curl -s -m 5 http://169.254.169.254/latest/dynamic/instance-identity/document | grep -q availabilityZone) ; then
    echo yes
  else
    echo no
  fi

fi

Obviously, you could expand this with even more fallback checks, and include paranoia about handling e.g. a false positive from /sys/hypervisor/uuid happening to start with "ec2" by chance and so on. But this is a good-enough solution for illustration purposes and probably nearly all non-pathological use-cases.

[†] Got back this explanation from AWS support about the change for c5/m5 instances:

The C5 and M5 instances use a new hypervisor stack and the associated kernel drivers do not create files in sysfs (which is mounted at /sys) as the Xen drivers used by the other/older instance types do. The best way to detect whether the operating system is running on an EC2 instance is to account for the different possibilities listed in the documentation you linked.

5
  • 6
    Yes fellow traveler in 2018... this is the answer you've been looking for. May 1, 2018 at 22:14
  • reading /sys/devices/virtual/dmi/id/product_uuid also requires root privileges
    – Thayne
    Jan 3, 2019 at 22:00
  • @Thayne correct -- that's what the comment above that elif block says, and that's why the elif test uses the -r test operator, which checks whether the file exists and that you have read permissions for the file. Jan 4, 2019 at 14:28
  • 1
    An additional note on the 169.254.169.254 metadata - it is not always ready at boot time. If you need that metadata for a bootscript, you're going to need to keep polling until it's ready. I've seen it take up to 30 seconds after the instance has started running its cloud-init bootscripts.
    – vacri
    Sep 26, 2019 at 6:12
  • This is awesome, except that I need to implement it in javascript :(. Probably not very relevant here on ServerFault, but I'll post it anyways when I translate this to js Mar 3, 2022 at 22:11
46

Changed Hannes' answer to avoid error messages and include example usage in script:

if [ -f /sys/hypervisor/uuid ] && [ `head -c 3 /sys/hypervisor/uuid` == ec2 ]; then
    echo yes
else
    echo no
fi

This doesn't work in Windows instances. Advantage over curl is that it's close to instantaneous on both EC2 and non-EC2.

Update: For new C5/M5 instances, use the file /sys/devices/virtual/dmi/id/product_uuid instead and check for EC2 instead of ec2. (Thanks to Josh and Saumitra in the comments for mentioning this)

10
  • 6
    AWS also seem to recommend doing it this way docs.aws.amazon.com/AWSEC2/latest/UserGuide/…
    – Mike
    Dec 27, 2016 at 18:00
  • 3
    I like this method. Just be aware that a non-EC2 system running under a hypervisor could generate a UUID that starts with ec2 -- a false positive. It’s unlikely (a 1-in-256 chance) and only if you are using a hypervisor that populates that file. That’s why the documentation linked above says “you are probably looking at an EC2 instance”.
    – Nate
    Mar 24, 2017 at 19:41
  • 2
    @Wildcard: I can’t edit my comment, but that’s right.
    – Nate
    Jul 19, 2017 at 7:16
  • 9
    DANGER! This method had worked reliably for us for years... until just recently, with the latest c5 and m5 types which do not have this file present. So I have to add a fallback check of 169.254.169.254 to handle those instances. Jan 3, 2018 at 19:11
  • 3
    For readers coming to this answer, the new C5/M5 family does not have /sys/hypervisor/uuid (At least on Latest AMI) use 2nd alternative in @Mike 's link docs.aws.amazon.com/AWSEC2/latest/UserGuide/… i.e. use the /sys/devices/virtual/dmi/id/product_uuid or dmidecode Apr 12, 2018 at 4:23
17

If the goal is to tell if it's an EC2 instance OR another kind of cloud instance, like google, then dmidecode works very nicely and no networking is required. I like this vs some of the other approaches because the metadata url path is different for EC2 and GCE.

# From a google compute VM
$ sudo dmidecode -s bios-version
Google

# From an amazon ec2 VM
$ sudo dmidecode -s bios-version
4.2.amazon
3
  • I'd expect this to work fine in other VM environments and even on real hardware - I don't expect any hardware vendors to ship systems where the bios version says "amazon"...
    – Guss
    Aug 14, 2016 at 16:20
  • On my Ubuntu EC2 instances this returns 1.0 - no mention of amazon.
    – Nate
    Aug 16, 2019 at 21:55
  • Nice solution. In my case i need to identify if instance is launched in EC2 or in Oracle cloud. dmidecode -s bios-version returned 1.0 which is not good. Played with dmidecode a bit and found this command $ sudo dmidecode -s chassis-asset-tag for me it returns either "Amazon EC2" or "OracleCloud.com". Jun 22, 2022 at 19:29
14

Look for the metadata by the EC2 internal domain name instead of IP, which will return a fast DNS failure if you're not on EC2 and this avoids IP conflicts and routing issues:

curl -s http://instance-data.ec2.internal && echo "EC2 instance" || echo "Non EC2 instance"

On some distros, very basic systems, or very early at installion stages curl is not available. Using wget instead:

wget -q http://instance-data.ec2.internal && echo "EC2 instance" || echo "Non EC2 instance"
4
  • 5
    Unfortunately, appears to fail in VPC!
    – amelia
    Feb 6, 2015 at 1:35
  • 2
    Also don't use the exclamation mark character inside double quotes -- your echo may blow up with -bash: !": event not found. Use single quotes for those echos instead. Nov 10, 2015 at 18:19
  • 1
    this probably assumes that the server is still using EC2s DNS servers that know about the ec2.internal zone and that nobody has changed /etc/resolv.conf to 8.8.8.8 or rolled their own DNS infastructure.
    – lamont
    Mar 31, 2016 at 1:35
  • 1
    AWS appears to have broken this. I can no longer resolve instance-data.ec2.internal. instance-data.us-west-2.compute.internal works, though, at least for now. Jun 24, 2016 at 18:59
6

Hostnames are likely to change, run a whois against your public IP:

if [[ ! -z $(whois $(curl -s shtuff.it/myip/short) | grep -i amazon) ]]; then 
  echo "I'm Amazon"
else 
  echo "I'm not Amazon"
fi

or hit the AWS meta-data url

if [[ ! -z $(curl -s http://169.254.169.254/1.0/) ]]; then 
  echo "I'm Amazon"
else 
  echo "I'm not Amazon"
fi
2
  • 2
    Add a --connect-timeout 1 to the second curl statement to fail quickly if you're not running on EC2. Sep 25, 2013 at 12:27
  • 1
    FWIW, using the metadata URL can indicate it's running as a cloud instance, but cannot conclusively determine if it is specifically EC2. OpenStack and Eucalyptus also use the same metadata URI. I know this is picking nits, but for my work, which cloud provider matters.
    – EmmEff
    Feb 20, 2014 at 15:53
5

This also works well for Linux hosts in ec2 and does not require the network and any related timeouts:

grep -q amazon /sys/devices/virtual/dmi/id/bios_version

This works, because Amazon defines this entry like so:

$ cat /sys/devices/virtual/dmi/id/bios_version 4.2.amazon

2
  • 2018-05-01; appears to be invalid on M5 instances running Ubuntu. May 1, 2018 at 17:46
  • On my Ubuntu EC2 instances this returns 1.0. No mention of amazon.
    – Nate
    Aug 16, 2019 at 21:56
3
test -f /sys/hypervisor/uuid -a `head -c 3 /sys/hypervisor/uuid` == ec2 && echo yes

but I don't know how portable this is across distributions.

5
  • 2
    Well, it's certainly not going to work on Windows EC2 instances.
    – ceejayoz
    Jun 4, 2015 at 3:24
  • 1
    I prefer this method since it doesn't involve a network interaction which can hang for all kinds of reasons. Using timeouts for an HTTP exchange is not guaranteed to prevent hangs. I don't care about Windows instances.
    – Hannes
    Jun 11, 2015 at 1:34
  • That is exactly what I needed! Way better than curl'ing something, thanks!
    – qwertzguy
    Jun 22, 2015 at 18:38
  • 1
    Consider using the full UUID, just in case some other vendor's hypervisor UUID also starts with "ec2". The chance of that happening is 1 in 4096 which is not negligible.
    – Hannes
    Jun 23, 2015 at 2:07
  • 1
    Actually, comparing the entire UUID does not work since I have seen multiple different hypervisor UUIDs in the wild. They all start with "ec2" though, so this answer works as is.
    – Hannes
    Aug 19, 2015 at 20:16
3

Quick answer:

if [[ -f /sys/devices/virtual/dmi/id/product_uuid ]] && \
    grep -q "^EC2" /sys/devices/virtual/dmi/id/product_uuid
then
    echo "IS EC2"
else
    echo "NOT EC2"
fi

I had been using one of the answers posted here for over a year - but it doesn't work on the new 'c5' instance types (I'm working on upgrading from 'c4' now).

I like this solution because it seems like the least likely to break in the future.

On the older instance types, and the newer ones, this file is present and starts with 'EC2'. I checked on Ubuntu running on VirtualBox (that I also need to support) and it contains the string 'VirtualBox'.

As a previous poster noted (but it was easy to miss) - there is Amazon documentation on ways to do this - which include my answer.

https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/identify_ec2_instances.html

2

Perhaps you can use "facter":

"Facter is a cross-platform library for retrieving simple operating system facts, like operating system, linux distribution, or MAC address."

http://www.puppetlabs.com/puppet/related-projects/facter/

For example, if we take a look to the ec2 fact (facter-1.6.12/lib/facter/ec2.rb):

require 'facter/util/ec2'
require 'open-uri'

def metadata(id = "")
  open("http://169.254.169.254/2008-02-01/meta-data/#{id||=''}").read.
    split("\n").each do |o|
    key = "#{id}#{o.gsub(/\=.*$/, '/')}"
    if key[-1..-1] != '/'
      value = open("http://169.254.169.254/2008-02-01/meta-data/#{key}").read.
        split("\n")
      symbol = "ec2_#{key.gsub(/\-|\//, '_')}".to_sym
      Facter.add(symbol) { setcode { value.join(',') } }
    else
      metadata(key)
    end
  end
end

def userdata()
  begin
    value = open("http://169.254.169.254/2008-02-01/user-data/").read.split
    Facter.add(:ec2_userdata) { setcode { value } }
  rescue OpenURI::HTTPError
  end
end

if (Facter::Util::EC2.has_euca_mac? || Facter::Util::EC2.has_openstack_mac? ||
    Facter::Util::EC2.has_ec2_arp?) && Facter::Util::EC2.can_connect?

  metadata
  userdata
else
  Facter.debug "Not an EC2 host"
end
2

Well actually, there is a very simple way to detect if the host is an EC2 instance: check the reverse lookup of your public IP. The EC2 reverses are quite hard to miss.

Also, if you did not modify it, the hostname should be your reverse, making it further easy to spot it.

You might also use the "magical IP" you talked about, as it is indeed the standard way to get EC2 Instance tags, however, if you are not on a EC2 network, you will have to wait for a timeout, which is generally not desirable...

If these methods are not enough, just do a whois of your IP and check if you are within and Amazon EC2 IP block.

EDIT: You may use this small shell bit:

#!/bin/bash
LOCAL_HOSTNAME=$(hostname -d)
if [[ ${LOCAL_HOSTNAME} =~ .*\.amazonaws\.com ]]
then
        echo "This is an EC2 instance"
else
        echo "This is not an EC2 instance, or a reverse-customized one"
fi

Careful though, [[ is a bashism. You may also use a Python or Perl uniline, YMMV.

3
  • 14
    this doesn't work in a VPC or an environment where you have changed the hostname; eg. if your machines are in domain.local Jan 28, 2013 at 9:28
  • 3
    the hostname bit is just bound to fail.
    – Dan Pritts
    Feb 13, 2014 at 19:28
  • 3
    hostname -d returns eu-west-1.compute.internal Feb 22, 2017 at 12:09
2

A bit late to this party, however I came across this post and then found this AWS documentation:

For a definitive and cryptographically verified method of identifying an EC2 instance, check the instance identity document, including its signature. These documents are available on every EC2 instance at the local, non-routable address http://169.254.169.254/latest/dynamic/instance-identity/

https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/identify_ec2_instances.html

This, of course, requires the network overhead though you can set the curl timeout like so:

curl -s --connect-timeout 5 http://169.254.169.254/latest/dynamic/instance-identity/

That sets the timeout to 5s.

1

If you have curl installed, this command will return 0 if you are running within EC2 and non-zero if you are not:

curl --max-time 3 http://169.254.169.254/latest/meta-data/ami-id 2>/dev/null 1>/dev/null`

It tries to pull the EC2 metadata declaring the AMI-ID. If this does not succeed after 3 seconds, it assumes it is not running in EC2.

0

I am impressed with the many answers to this question. Reading them has convinced me there is currently no simple and reliable answer. I hope Amazon provides a better solution; meanwhile, here is my simple answer (for *NIX systems) to the original question, How to know if a machine is an EC2 instance:

test -x /usr/bin/aws && echo This is an AWS instance || echo This is not an AWS instance

Caveat: I suppose someone may install the AWS CLI on a non-EC2 instance, but it does seem unlikely.

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .