Creating, attaching, formatting, mounting, etc. an EBS volume(s) for Ubuntu Linux during CFT stack creation using UserData...


I am trying to create a disk/drive during the creation of my Ubuntu system while running my Cloudformation Template. I am using the "UserData" section to initialize the resource. Below is the UserData "bash" commands that I've tried. I seem to be missing something. How can you "map" the "/dev/xvdb" device to the "nvme[n]n1" moniker. How can this be done? Thoughts?

I found a number of resources (as well as other posts), but they don't appear to be effective: ...

UserData section(s):

"UserData"       : { "Fn::Base64" : { "Fn::Join" : ["", [
    "mkdir /data\n",
    "blkid $(readlink -f /dev/xvdb) || mkfs -t ext4 $(readlink -f /dev/xvdb)",
    "e2label $(readlink -f /dev/xvdb) aro-data",
    "sed  -e '/^[\/][^ \t]*[ \t]*\/data[ \t]/d' /etc/fstab",
    "grep -q ^LABEL=aro-data /etc/fstab || echo 'LABEL=aro-data /data ext4 defaults' >> /etc/fstab",
    "grep -q \"^$(readlink -f /dev/xvdb) /data \" /proc/mounts || mount /data",

... or

"UserData"       : { "Fn::Base64" : { "Fn::Join" : ["", [
   "mkdir /data\n",
   "mkfs -t ext4 /dev/nvme1n1\n",      <-- Used "/dev/nvme0n1" too though I know it changes
   "echo $(blkid -o export /dev/nvme1n1 | grep ^UUID=)       /data  ext4    defaults,nofail 0       2 >> /etc/fstab\n",
   "mount -a\n",


This is what I see once the instance is created:

loop0         7:0    0   97M  1 loop /snap/core/9665
loop1         7:1    0 28.1M  1 loop /snap/amazon-ssm-agent/2012
loop2         7:2    0 55.7M  1 loop /snap/core18/2745
loop3         7:3    0 24.4M  1 loop /snap/amazon-ssm-agent/7269
nvme0n1     259:0    0  100G  0 disk
└─nvme0n1p1 259:1    0  100G  0 part /
nvme1n1     259:2    0  500G  0 disk       <-- So the volume appears to have been created and attached. But not mounted?

-- These are the sections outlining the volumes:

"BlockDeviceMappings" : [
        "DeviceName" : "/dev/sda1",
        "Ebs" : { 
            "VolumeSize" : {"Ref": "RootVolumeSize"},
            "VolumeType" : "gp3"

"Volumes" : [
    { "VolumeId" : { "Ref" : "DataVolume" }, "Device" : "/dev/xvdb" }

"DataVolume" : {
    "Type" : "AWS::EC2::Volume",
    "Properties" : {
        "Size" : {"Ref": "DataVolumeSize"},
        "Encrypted" : "true",
        "AvailabilityZone" : { "Fn::FindInMap" : [ "RegionMap", { "Ref" : "AWS::Region" }, "az" ]},
        "VolumeType" : "gp3"
"dataattachment" : {
    "Type" : "AWS::EC2::VolumeAttachment",
    "Properties" : {
       "InstanceId" : { "Ref" : "EC2Instance" },
       "VolumeId"  : { "Ref" : "DataVolume" },
       "Device" : "/dev/xvdb"

3 Answers

Once the instance has booted can you check that the user data script is created correctly? You can see this via the Console or by logging on to the instance in /var/lib/cloud/instances/i-*.

It looks like there are some '/n' missing from the script above.

If you find the user-data.txt file in /var, copy to /tmp as, chmod 755, then run it by hand to see if there are errors.

profile pictureAWS
answered 5 months ago
  • Didn't solve the issue, but great information. I didn't know about the "copy" of the user-data.txt and that's extremely useful - very helpful for debugging. I did correct the missing '/n's, but that didn't seem to be the issue.


If you have only two block devices then the second device is always going to be the last disk device row of the lsblk output. So extract the name by chopping up the output of the command, e.g.

$ SECOND_DISK=/dev/`lsblk -l | grep -w disk | tail -1 | awk '{print $1}'`

Then use this variable in the commands to create and mount the volume, updating the fstab, and so on.

profile picture
answered 5 months ago
  • Thanks, but I have one question: The "lsblk" output order, per the man pages, doesn't seem to agree on the "order" of output. Why do you say that it will always be the drive I'm looking for?

           The default output, as well as the default output from options
           like --fs and --topology, is subject to change. So whenever
           possible, you should avoid using default outputs in your scripts.
           Always explicitly define expected columns by using --output
           columns-list and --list in environments where a stable output is

    I ran this 3 times and it worked, the next 2 times it didn't (the nvme?n1 was already "taken"). So it seems like it isn't always the last drive listed. Any thoughts on this? I was hoping to map the device setting in the cft to the nvme?n1, but it doesn't seem to be possible. I'll try -o the output and sort it somehow.


Well, I'm posting what I went with, though I'm still not happy as there is still no "linkage" between the CFT's Volume Device name ("/dev/xvdb") and the "nvme?n1" disk in the UserData. It's attached so there should be a way to definitively link the two. However, by using the previous two comments, I was able to craft the following UserData bash code to "more often than not" mount the attached EBS volume. Maybe this will help somebody, or get them closer to the actual answer. I left in the "debugging" echo's as they were helpful to show what was being done (as per the first answer). The main problem is that the "lsblk" call is a best guess as to which disk I'll get. And my EBS Root Volume Size "cannot/should not" be the same size as my EBS Data Volume Size:

"UserData": { "Fn::Base64" : { "Fn::Join" : ["", [
    "echo Used for debugging - can be deleted at any time >> /build.txt\n",
    "mkdir /data\n",
    "DATA_DISK=$(lsblk -l | grep -w disk | grep ",
    { "Ref" : "DataVolumeSize" },
    " | tail -1 | awk '{print $1}')\n",
    "echo $(lsblk -l) >> /build.txt\n",
    "echo DATA_DISK = $DATA_DISK >> /build.txt\n",
    "mkfs -t ext4 /dev/$DATA_DISK\n",
    "echo $(blkid -o export /dev/$DATA_DISK | grep ^UUID=)       /data  ext4    defaults,nofail 0       2 >> /etc/fstab\n",
    "echo $(blkid -o export /dev/$DATA_DISK | grep ^UUID=)       /data  ext4    defaults,nofail 0       2 >> /build.txt\n",
    "echo blkid= $(blkid -o export /dev/$DATA_DISK)  >> /build.txt\n",
    "mount -a\n",
    "echo $(cat /etc/fstab) >> /build.txt\n",

P.S. I saw ( where someone was using:

    "mkdir /data\n",
    "blkid $(readlink -f /dev/xvdb) || mkfs -t ext4 $(readlink -f /dev/xvdb)\n",
    "e2label $(readlink -f /dev/xvdb) xxx-data\n",
    "sed  -e '/^[\/][^ \t]*[ \t]*\/data[ \t]/d' /etc/fstab\n",
    "grep -q ^LABEL=xxx-data /etc/fstab || echo 'LABEL=xxx-data /data ext4 defaults' >> /etc/fstab\n",
    "grep -q \"^$(readlink -f /dev/xvdb) /data \" /proc/mounts || mount /data\n",

It never worked for me and seemed to always return a blank. BTW, I'm on Ubuntu, so maybe that has something to do with it.

answered 5 months ago

You are not logged in. Log in to post an answer.

A good answer clearly answers the question and provides constructive feedback and encourages professional growth in the question asker.

Guidelines for Answering Questions