Ansible and AWS: Mounting NVMe volumes dynamically

Our SaaS offering is based on services provided by AWS. We are using Ansible to automatically configure our infrastructure. In doing so, we hit an interesting obstacle when dealing with NVMe-backed (SSD) EBS volumes:

First, we use the ec2_vol plugin to set up and attach the volume:

					- name: Create EC2 volume
    volume_size: "16G"
    volume_type: "gp2"
    instance: "{{ instance_id }}"
    device_name: sdf
    name: "some-data"
      purpose: "{{ purpose }}"
    state: present
  register: ec2_volume

According to the docs, the volume should now appear under the device_name, in this case /dev/sdf. However, this does not seem to be the case.

First thing, the device names are assigned by the kernel and are not predictable. Second thing, NVMe SSDs are following the naming scheme of   /dev/nvmeXnY. The documentation from AWS concerning EBS/NVMe points out this fact and explains that the device_name is actually stored in a vendor-specific extension of the NVMe controller.

How to mount a volume that has just been created dynamically?

The device name is assigned somewhat randomly, and properties like UUIDs are not yet known for new volumes: how can we still access the correct volume? There are some solutions, but they require quite some shell scripting. I want to show a simpler approach, using only built-in Ansible tools:

We can exploit that AWS assigns an identifier like vol-abcdef0123456 to the volume. Inside the EC2 instance, this identifier appears as the serial number of the volume, but without the hyphen, e.g. volabcdef0123456.

Let’s retrieve the volume information from AWS…

					- name: Get EC2 volume details
  delegate_to: localhost
      "tag:Name": "some-data"
  register: ec2_vol_info

… and extract the volume ID:

					- name: Store volume serial number
    ebs_volume_id: "{{ ec2_vol_info.volumes[0].id | replace('-', '') }}"

Now we can use lsblk to obtain both the device path and the serial numbers of all volumes on the instance. We filter the output for the serial number of our EBS volume:

					- name: List Linux block devices
    cmd: "lsblk -o PATH,SERIAL | grep {{ ebs_volume_id }} | cut -d' ' -f1"
  register: lsblk

- name: Store EBS volume device name
    ebs_volume_device_name: "{{ lsblk.stdout }}"

The results

And voilà, the fact ebs_volume_device_name now contains the full path to our volume. This can be used to format and mount the volume:

					- name: Format EC2 volume
    fstype: ext4
    dev: "{{ ebs_volume_device_name }}"
    force: no

- name: Mount EC2 volume
    path: /mnt/ebs
    src: "{{ ebs_volume_device_name }}"
    fstype: ext4
    boot: yes
    state: mounted

As you see, aside from the one-line shell invocation around lsblk only well-readable and maintainable Ansible-Plugins were necessary.

This approach works across reboots of the instance and is in line with Ansible’s idempotent design.

© 2024 flowciety GmbH


In particle physics, gluons are elementary particles responsible for attracting protons and neutrons in an atomic nucleus.

Like particles at the subatomic level, Glu:on helps companies build a strong bond with their partners.