Packages

Read instance metadata from AWS, GCE, Azure, Oracle, Hetzner, Scaleway, and OpenStack through one API.

Current section

Files

Jump to
imds README.md
Raw

README.md

<!--
SPDX-FileCopyrightText: 2026 Łukasz Niemier <~@hauleth.dev>

SPDX-License-Identifier: Apache-2.0
-->

# IMDS

`imds` reads instance metadata from multiple cloud providers through one Elixir
API using the provider's instance metadata service.

## Use

```elixir
{:ok, identity} = IMDS.identity()
```

`IMDS.identity/1` returns:

```elixir
%{
  provider:
    :aws
    | :gce
    | :azure
    | :oracle
    | :hetzner
    | :scaleway
    | :openstack
    | :digital_ocean
    | :akamai
    | :vultr,
  instance_id: binary() | nil,
  instance_type: binary() | nil,
  region: binary() | nil,
  zone: binary() | nil,
  scope_id: binary() | nil,
  raw: map()
}
```

The `raw` field holds the provider identity document when available.

AWS reads the instance type and the account ID from the instance identity
Detection requires AWS IMDSv2. On an instance that permits only IMDSv1, pass
`provider: :aws` or set `INSTANCE_METADATA_PROVIDER=aws`.

Use a provider name to skip discovery:

```elixir
{:ok, identity} = IMDS.identity(provider: :gce)
```

Use `IMDS.detect/1` to find the provider without reading its identity:

```elixir
{:ok, :azure} = IMDS.detect()
```

Use `IMDS.get/2` and `IMDS.get_json/2` to read a provider path. For Azure,
`IMDS.get/2` adds the `api-version` query parameter when the path does not hold
one.

```elixir
{:ok, id} = IMDS.get("/computeMetadata/v1/instance/id")
{:ok, attributes} =
  IMDS.get_json("/computeMetadata/v1/instance/attributes/?recursive=true")
```

## Logger metadata

`IMDS.set_global_logger_metadata/1` copies the identity to the primary logger
metadata, so every log line holds the provider and the instance:

```elixir
:ok = IMDS.set_global_logger_metadata()
```

The function sets the keys `:provider`, `:instance_id`, `:instance_type`,
`:region`, `:zone`, and `:scope_id`. It does not set `:raw`. It does not set a
field that the provider does not report, so the metadata holds no `nil` value.
It does not replace a key that the logger metadata already holds.

It accepts the same options as `identity/1`. It returns `{:error, reason}` and
changes nothing when detection or the identity read fails.

Call it one time at application start. Without `:provider` or
`INSTANCE_METADATA_PROVIDER`, each call probes all providers.

## Providers

| Provider | Metadata service | Request requirement |
|---|---|---|
| AWS EC2 | IMDSv2 | Token from `PUT /latest/api/token`; token header on metadata GETs |
| GCE | VM metadata server | `Metadata-Flavor: Google` header |
| Azure | Instance Metadata Service (IMDS) | `Metadata: true` header and API version `2021-02-01` |
| Oracle Cloud Infrastructure | IMDSv2 | `Authorization: Bearer Oracle` header |
| Hetzner Cloud | Metadata service | No header; plain text values below `/hetzner/v1/metadata` |
| Scaleway | Instance metadata service | No header; JSON document at `/conf?format=json` |
| OpenStack (nova) | Metadata service | No header; JSON document at `/openstack/latest/meta_data.json` |
| DigitalOcean | Droplet metadata service | No header; JSON document at `/metadata/v1.json` |
| Akamai Cloud Computing | Metadata service | Token from `PUT /v1/token`; `Metadata-Token` header and `Accept: application/json` on `/v1/instance` |
| Vultr | Instance metadata service | No header; JSON document at `/v1.json` |

The module keeps no state between calls. Every call of `IMDS.detect/1` probes
the providers again. The environment variable `INSTANCE_METADATA_PROVIDER` can
select provider and omit probing. The `:provider` option takes precedence over
the environment variable.

## Options

| Option | Default | Purpose |
|---|---|---|
| `:provider` | detection | Select one provider and skip detection |
| `:base_url` | provider address | Replace the provider base URL, for example in a local test |
| `:timeout` | 5,000 ms | Timeout for one request; discovery uses 1,000 ms unless set |
| `:req_options` | `[]` | Options added to the `Req` request, for example a test plug |
| `:aws_imds_v1_fallback` | `true` | Permit AWS IMDSv1 after token requests answer 403, 404 or 405 |
| `:api_version` | `2021-02-01` | Azure IMDS API version |

## License

Apache 2.0