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
  amazon.aws.ec2_vol:
    volume_size: "16G"
    volume_type: "gp2"
    instance: "{{ instance_id }}"
    device_name: sdf
    name: "some-data"
    tags:
      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
  amazon.aws.ec2_vol_info:
    filters:
      "tag:Name": "some-data"
  register: ec2_vol_info
				
			

… and extract the volume ID:

				
					- name: Store volume serial number
  ansible.builtin.set_fact:
    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
  ansible.builtin.shell:
    cmd: "lsblk -o PATH,SERIAL | grep {{ ebs_volume_id }} | cut -d' ' -f1"
  register: lsblk

- name: Store EBS volume device name
  ansible.builtin.set_fact:
    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
  community.general.filesystem:
    fstype: ext4
    dev: "{{ ebs_volume_device_name }}"
    force: no

- name: Mount EC2 volume
  ansible.posix.mount:
    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.

© 2022 flowciety GmbH

Gluon

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.