Gary Richardson
Gary Richardson

Reputation: 16441

Find region from within an EC2 instance

Is there a way to look up the region of an instance from within the instance?

I'm looking for something similar to the method of finding the instance id.

Upvotes: 198

Views: 154423

Answers (30)

SteveGoob
SteveGoob

Reputation: 1350

AWS hosts a metadata endpoint that has tons of useful information regarding your EC2 instance. For a long time, you'd have to parse out the region information indirectly, but now AWS has a dedicated route for region information: latest/meta-data/placement/region.

However, they've also introduced a new mechanism to authenticate to the metadata endpoint with IMDSv2. You first need a metadata token from the server, which you can get with the following:

TOKEN=$(curl -X PUT "http://169.254.169.254/latest/api/token" -H "X-aws-ec2-metadata-token-ttl-seconds: 3600")

Once you have a token, just pass it along with your request for the region endpoint:

curl -H "X-aws-ec2-metadata-token: $TOKEN" http://169.254.169.254/latest/meta-data/placement/region

For more information, check out AWS's docs.

EDIT (Dec 2021): It's also probably worth mentioning that this endpoint was made available in the 2019-10-01 release of the metadata API. Make sure your instance supports that version or later before using this by checking http://169.254.169.254/.

EDIT (Jun 2024): This answer used to be using IMDSv1, as that was the only option. It's been updated to reflect the new IMDSv2 pattern. The original answer follows for posterity.


At some point since most of these answers have been posted, AWS did the reasonable thing and implemented a new path: latest/meta-data/placement/region.

This means getting the region should be as simple as

curl http://169.254.169.254/latest/meta-data/placement/region

Upvotes: 49

Hong Phuc
Hong Phuc

Reputation: 83

Update 03-25-2024

For those who use Amazon Linux 2023 instance type, the command

curl -s http://169.254.169.254/latest/meta-data/placement/availability-zone

will get 401 error. Below is my command to get AZ info

TOKEN=`curl -X PUT "http://169.254.169.254/latest/api/token" -H "X-aws-ec2-metadata-token-ttl-seconds: 21600"` && curl -H "X-aws-ec2-metadata-token: $TOKEN" http://169.254.169.254/latest/meta-data/placement/availability-zone

Upvotes: 1

Matt Lo
Matt Lo

Reputation: 5731

2024 - There are two types of calls

IMDSv2 (Recommended)

You must retrieve an auth token before getting meta data. The following call will set an environment variable for you to reuse.

TOKEN=`curl -X PUT "http://169.254.169.254/latest/api/token" -H "X-aws-ec2-metadata-token-ttl-seconds: 21600"`

Use the auth token to get the region (with other APIs calls too)

curl http://169.254.169.254/latest/meta-data/placement/region -H "X-aws-ec2-metadata-token: $TOKEN"

IMDSv1

No token is required in v1. Only works if v2 is set to optional (see screenshot below). You will see an empty string response if you try v1 in a v2-only environment.

curl http://169.254.169.254/latest/meta-data/placement/region

How do I know which version to use?

Use v2 if you can. If you have legacy instances and need this data now, use v1 with a roadmap to migration.

v1

Region vs Availability Zone (us-east-1 vs us-east-1a)

  • Region - Regional location of the instance. Returns the same region data as on AWS's region dropdown. Examples: us-east-2, ap-southeast-1.
  • Availability Zone - Returns the specific zone within a region, denoted by the character at the end of a region. This will mirror the subnet mapped to the instance. Use the availability zone API call instead of the region API call to get this data. Examples: us-east-2a, us-east-2c, ap-southeast-1b

Docs: https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/instancedata-data-categories.html

What is 169.254.169.254?

It's a special-use address. It only works within the EC2 instance.

Detailed answer: https://serverfault.com/questions/427018/what-is-this-ip-address-169-254-169-254

Upvotes: 6

Vishnu Vivek
Vishnu Vivek

Reputation: 2121

According to the latest IMDSv2, we need to get the token and use the token while retrieving the metadata.

TOKEN=`curl -X PUT "http://169.254.169.254/latest/api/token" -H "X-aws-ec2-metadata-token-ttl-seconds: 21600"`
EC2_AVAIL_ZONE=$(curl -H "X-aws-ec2-metadata-token: $TOKEN" http://169.254.169.254/latest/meta-data/placement/availability-zone)
EC2_REGION=$(curl -H "X-aws-ec2-metadata-token: $TOKEN" http://169.254.169.254/latest/meta-data/placement/region)

Upvotes: 1

dannosaur
dannosaur

Reputation: 2639

That URL (http://169.254.169.254/latest/dynamic/instance-identity/document) doesn't appear to work anymore. I get a 404 when I tried to use it. I have the following code which seems to work though:

EC2_AVAIL_ZONE=`curl -s http://169.254.169.254/latest/meta-data/placement/availability-zone`
EC2_REGION="`echo \"$EC2_AVAIL_ZONE\" | sed 's/[a-z]$//'`"

Upvotes: 193

Alain O'Dea
Alain O'Dea

Reputation: 21686

If you are using IMDSv2, you'll need the token first.

Here's an example using bash, which also depends on curl:

function get-aws-region() {
  imdsv2_token="$(
    curl -s -X PUT "http://169.254.169.254/latest/api/token" \
            -H "X-aws-ec2-metadata-token-ttl-seconds: 1"
  )"
  curl -s http://169.254.169.254/latest/meta-data/placement/region \
         -H "X-aws-ec2-metadata-token: $imdsv2_token"
}

This gets a very short-lived token and uses it to get the region.

Upvotes: 1

Sagi Mann
Sagi Mann

Reputation: 3610

All this no longer works on AMI Linux 2... I found this offline (undocumented) approach:

REGION=`cat /opt/elasticbeanstalk/config/ebenvinfo/region`
echo $REGION

# output example:
us-east-1

Upvotes: 1

puravidaso
puravidaso

Reputation: 1223

If you are looking for a simpler way to do it, you can look at /etc/resolv.conf and find a line like "search us-west-2.compute.internal". For example:

$ grep "^search" /etc/resolv.conf | sed "s:.* ::; s:\..*::"
us-west-2

Upvotes: 1

denken
denken

Reputation: 1

For the sed and curl solution it looks like format has changed a bit. For me works

curl -s http://169.254.169.254/latest/dynamic/instance-identity/document | sed -n 's/ "region" : "\(.*\)"[,]/\1/p'

Upvotes: 0

Chart96
Chart96

Reputation: 480

A method using only egrep, which should work on most any linux instance spun up without having to install any extra tooling. I tested this against a list of all current AWS regions and they all match.

curl http://169.254.169.254/latest/meta-data/placement/availability-zone | egrep -o '(\w)+-(\w)+-[0-9]'

Explanation of the REGEX:

  • "(\w)+" This matches any number of letters
  • "-" matches only a single dash
  • "[0-9]" matches any 1 number

If you want this into a variable do:

region=$(curl http://169.254.169.254/latest/meta-data/placement/availability-zone | egrep -o '(\w)+-(\w)+-[0-9]')

Upvotes: 0

Jose Alban
Jose Alban

Reputation: 7926

ec2-metadata --availability-zone | sed 's/.$//'

For debian based systems, the command is without dash.

ec2metadata --availability-zone | sed 's/.$//'

Upvotes: 52

Garen
Garen

Reputation: 177

ec2metadata (no dash) is the current command to provide you all the aws hosting info about your ec2 box. this is the most elegant and secure approach. (ec2-metadata is the old, no longer valid command.)

Upvotes: 0

cwa
cwa

Reputation: 1142

If you're running on windows, you can use this powershell one-liner:

$region=(Invoke-RestMethod "http://169.254.169.254/latest/dynamic/instance-identity/document").region

Upvotes: 3

Ajax
Ajax

Reputation: 63

Was also looking for a solution to find region from the instance and here is my pure Bash solution:

az=$(curl -s http://169.254.169.254/latest/meta-data/placement/availability-zone)
region=${az:0:${#az}-1}

unless there are regions where AZ has more than two letters, which I'm not aware of.

Upvotes: 1

dank
dank

Reputation: 589

For anyone wanting to do this with good ol powershell

$var = (curl http://169.254.169.254/latest/dynamic/instance-identity/document | Select-String-Pattern "Zone" | ConvertFrom-Json | Select-Object -ExpandProperty "region")
echo $var

Upvotes: 4

rec
rec

Reputation: 31

This works for eu-central-1 as well as the various letter zones. (I don't have enough rep to reply to the sed answer above)

ec2-metadata --availability-zone | sed 's/[a-z]$//'

Upvotes: 3

Gil Zellner
Gil Zellner

Reputation: 979

2 liner that works as long as you are using ec2.internal as your search domain:

az=$(curl -s http://instance-data/latest/meta-data/placement/availability-zone)
region=${az:0:${#az} - 1}

Upvotes: 4

Surya Prakash Patel
Surya Prakash Patel

Reputation: 731

If you are looking to get region using JS, this should work :

meta.request("/latest/meta-data/placement/availability-zone",function(err,data){
        if(err)
                console.log(err);
        else{
                console.log(data);
                str = data.substring(0, data.length - 1);
                AWS.config.update({region:str});
                ec2 = new AWS.EC2();
            }
     });

This was the mapping found from AWS DOCS, in response to metadata API call, just trim the last character should work.

  eu-west-1a :eu-west-1
  eu-west-1b :eu-west-1
  eu-west-1c :eu-west-1
  us-east-1a :us-east-1
  us-east-1b :us-east-1
  us-east-1c :us-east-1
  us-east-1d :us-east-1
  ap-northeast-1a :ap-northeast-1
  ap-northeast-1b :ap-northeast-1
  us-west-1a :us-west-1
  us-west-1b :us-west-1
  us-west-1c :us-west-1
  ap-southeast-1a :ap-southeast-1
  ap-southeast-1b :ap-southeast-1

Upvotes: 1

Alan
Alan

Reputation: 51

If you work with json - use right tools. jq much powerful in this case.

# curl -s curl -s http://169.254.169.254/latest/dynamic/instance-identity/document | jq -r '.region'
eu-west-1

Upvotes: 5

mohrt
mohrt

Reputation: 504

Get the region from the availability zone, strip off the last letter of it.

ec2-metadata -z | awk '{print $2}' | sed 's/[a-z]$//'

Upvotes: 9

Jaeyoung Chun
Jaeyoung Chun

Reputation: 671

If you want to avoid regular expression, here's a one-liner you can do with Python:

curl -s http://169.254.169.254/latest/dynamic/instance-identity/document | python -c "import json,sys; print json.loads(sys.stdin.read())['region']"

Upvotes: 27

Francesco Gualazzi
Francesco Gualazzi

Reputation: 939

Easiest I found so far

 curl -s 169.254.169.254/latest/meta-data/placement/availability-zone | sed 's/.$//'

Upvotes: 18

Ilya Sher
Ilya Sher

Reputation: 601

If you are OK with using jq, you can run the following:

curl -s http://169.254.169.254/latest/dynamic/instance-identity/document | jq .region -r

I guess it's the cleanest way.

Upvotes: 47

Steve Jansen
Steve Jansen

Reputation: 9494

Thanks to https://unix.stackexchange.com/a/144330/135640, with bash 4.2+ we can just strip the last char from the availability zone:

$ region=`curl -s 169.254.169.254/latest/meta-data/placement/availability-zone`
$ region=${region::-1}
$ echo $region
us-east-1

This assumes AWS continues to use a single character for availability zones appended to the region.

Upvotes: 4

Kelly Setzer
Kelly Setzer

Reputation: 304

This is the cleanest solution I found:

curl -s http://169.254.169.254/latest/dynamic/instance-identity/document |sed -n 's/  "region" : "\(.*\)"/\1/p'

E.g.,

export REGION=$(curl -s http://169.254.169.254/latest/dynamic/instance-identity/document |sed -n 's/  "region" : "\(.*\)"/\1/p')

  • Doesn't make an API call, uses EC2 instance meta-data
  • Only uses curl, and basic sed, so no dependencies on SDKs or tools not likely to be installed.
  • Doesn't attempt to parse the Availability Zone name, so no worries if AWS changes AZ/Region name format

Upvotes: 4

Daniel Kuppitz
Daniel Kuppitz

Reputation: 10904

You can use ec2-metadata:

ec2-metadata -z | grep -Po "(us|sa|eu|ap)-(north|south|central)?(east|west)?-[0-9]+"

Upvotes: 18

Ken Weiner
Ken Weiner

Reputation: 41

If you're able to use the AWS Java SDK, there is now a method that will return the current region name (such as "us-east-1", "eu-west-1"):

http://docs.aws.amazon.com/AWSJavaSDK/latest/javadoc/com/amazonaws/regions/Regions.html#getCurrentRegion()

Upvotes: 4

Ravi Kumar
Ravi Kumar

Reputation: 141

very simple one liner

export AVAILABILITY_ZONE=`wget -qO- http://instance-data/latest/meta-data/placement/availability-zone`
export REGION_ID=${AVAILABILITY_ZONE:0:${#AVAILABILITY_ZONE} - 1}

Upvotes: 14

mgarman
mgarman

Reputation: 1085

There is one more way of achieving that:

REGION=`curl http://169.254.169.254/latest/dynamic/instance-identity/document|grep region|awk -F\" '{print $4}'`

echo $REGION

us-east-1

Upvotes: 96

flaccid
flaccid

Reputation: 31

Or don't make Ubuntu or this tool a requirement and simply do:

: "${EBS_VOLUME_AVAILABILITY_ZONE:=$(curl -s http://169.254.169.254/latest/meta-data/placement/availability-zone)}"
: ${EBS_VOLUME_REGION:="${EBS_VOLUME_AVAILABILITY_ZONE%%*([![:digit:]])}"}

Upvotes: 3

Related Questions