Create, attach and mount a managed disk to an Azure Linux Virtual Machine.

We’re going to add a new data disk to an existing Azure Linux Virtual Machine and then mount it. We will be using CentOS 7.7 and will go through creating the Virtual Machine without the data disk, modify the ARM template to add the data disk. Then we will SSH into the VM and mount the new disk.

The plan

  1. Create our existing Virtual Machine with an ARM template
  2. Modify the ARM template to add a data disk
  3. SSH into the VM and mount the data disk

1. Create our existing Virtual Machine with an ARM template

The ARM template

This is our existing Virtual Machine without the data disk.

Take the following json and save it somewhere and name it new-vm.json

{
    "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
    "contentVersion": "1.0.0.0",
    "parameters": {
      "storageName": {
        "type": "string",
        "defaultValue": "teststorage",
        "metadata": {
          "description": "Name for our storage account"
        }
      },
      "vmName": {
        "type": "string",
        "defaultValue": "testvm1",
        "metadata": {
          "description": "Name for our Virtual Machine"
        }
      }
    },
    "variables": {
    },
    "resources": [
      {
        "name": "[concat(parameters('storageName'), uniqueString(resourceGroup().id))]",
        "type": "Microsoft.Storage/storageAccounts",
        "location": "[resourceGroup().location]",
        "apiVersion": "2015-06-15",
        "properties": {
          "accountType": "Standard_LRS"
        }
      },
      {
        "name": "test-net",
        "type": "Microsoft.Network/virtualNetworks",
        "location": "[resourceGroup().location]",
        "apiVersion": "2015-06-15",
        "properties": {
          "addressSpace": {
            "addressPrefixes": [
              "10.0.0.0/16"
            ]
          },
          "subnets": [
            {
              "name": "frontendSubnet",
              "properties": {
                "addressPrefix": "10.0.1.0/24"
              }
            }
          ]
        }
      },
      {
        "apiVersion": "2016-03-30",
        "type": "Microsoft.Network/publicIPAddresses",
        "name": "test-publicip",
        "location": "[resourceGroup().location]",
        "properties": {
          "publicIPAllocationMethod": "Dynamic"
        }
      },
      {
        "name": "[concat(parameters('vmName'),'-nic0')]",
        "type": "Microsoft.Network/networkInterfaces",
        "location": "[resourceGroup().location]",
        "apiVersion": "2017-06-01",
        "dependsOn": [
          "[concat('Microsoft.Network/publicIPAddresses/','test-publicip')]",
          "[concat('Microsoft.Network/virtualNetworks/','test-net')]"
        ],
        "properties": {
          "ipConfigurations": [
            {
              "name": "ipconfig",
              "properties": {
                "privateIPAllocationMethod": "Dynamic",
                "publicIPAddress": {
                  "id": "[resourceId('Microsoft.Network/publicIPAddresses','test-publicip')]"
                },
                "subnet": {
                  "id": "[concat(resourceId('Microsoft.Network/virtualNetworks', 'test-net'),'/subnets/','frontendSubnet')]"
                }
              }
            }
          ]
        }
      },
      {
        "name": "[concat(parameters('vmName'))]",
        "type": "Microsoft.Compute/virtualMachines",
        "location": "[resourceGroup().location]",
        "apiVersion": "2017-03-30",
        "dependsOn": [
          "[concat('Microsoft.Network/publicIPAddresses/','test-publicip')]",
          "[concat('Microsoft.Network/virtualNetworks/','test-net')]",
          "[concat('Microsoft.Network/networkInterfaces/',parameters('vmName'),'-nic0')]"
        ],
        "properties": {
          "hardwareProfile": {
            "vmSize": "Basic_A1"
          },
  
          "osProfile": {
            "computerName": "[parameters('vmName')]",
            "adminUsername": "YOURUSERNAME",
            "adminPassword": "Your_PASSWORD12345678"
          },
          "storageProfile": {
            "imageReference": {
              "publisher": "OpenLogic",
              "offer": "CentOS",
              "sku": "7.7",
              "version": "latest"
            },
            "osDisk": {
              "osType": "Linux",
              "name": "[concat(parameters('vmName'),'-','osdisk')]",
              "createOption": "FromImage",
              "caching": "ReadWrite"
            }
          },
          "networkProfile": {
            "networkInterfaces": [
              {
                "id": "[concat(resourceId('Microsoft.Network/networkInterfaces',concat(parameters('vmName'))),'-nic0')]"
              }
            ]
          }
        }
      }
    ],
    "outputs": {
    }
  }

Deploy our ARM template

And our Powershell file (deploy-new-vm.ps1):

$subscriptionId = "<YOUR_SUBSCRIPTION_ID>"
Select-AzureRmSubscription -SubscriptionId $subscriptionId

$resourceGroup = "test-infra"
$location = "North Europe"

New-AzureRmResourceGroup -Name $resourceGroup -Location $location

New-AzureRmResourceGroupDeployment `
    -Name test-infra-deployment `
    -ResourceGroupName $resourceGroup `
    -TemplateFile new-vm.json `
    -Verbose -Force

Deploy it!

Pull up a PowerShell prompt and make sure you have already run Login-AzureRmAccount, browse to your folder where our two new files are located and run them.

cd c:\dev\Create-VM-with-ARM\
.\deploy-new-vm.ps1

2. Modify the ARM template to add a data disk

Now that we have our Virtual Machine up and running, we need to add our data disk.

Modify the new-vm.json file and create a new object called dataDisks.

The following code will add a new 32GB data disk to the Virtual Machine.

 "dataDisks": [
                {
                    "name": "[concat(parameters('vmName'),'-data-0')]",
                    "diskSizeGB": 32,
                    "caching": "ReadWrite",
                    "lun": 0,
                    "managedDisk": {
                        "storageAccountType": "Standard_LRS"
                    },
                    "createOption": "Empty"
                }
            ]

Save the new-vm.json file and deploy it again as you did above by running .\deploy-new-vm.ps1

3. SSH into the VM and mount the data disk

SSH into the Virtual Machine

ssh <YOUR-VM-IP-ADDRESS>

Elevate privileges

First let’s elevate privileges to run the next commands as the root user

sudo su

List all the SCSCI disks attached to the Virtual Machine

dmesg | grep SCSI

dmesg output

Use fdisk to partition

From the output above sdc is our new drive.

So run the following command to partition the sdc disk:

fdisk /dev/sdc

1. Create new partition

Type n and hit enter to create the new parition.

2. Select primary partition

Type p and hit enter to choose a primary parition

3. Choose default values

Hit enter 3 times to choose the default values for the start, end of the partition and the partition number.

4. Write the changes to the disk

Hit w and enter to write the changes to the disk

fdisk output

Create new filesystem

Now we are going to create a new filesystem with type ext4 on the /dev/sdc1 parition

1. Create ext4 filesystem

mkfs -t ext4 /dev/sdc1

2. Create new folder and mount drive

mkdir /data
mount /dev/sdc1 /data
cd /data
ls

mount output

As we can see the lost+found in the list of items it means we have mounted the drive successfully.

Mount automatically on startup

We now need to make sure that the disk is mounted automatically on startup, in order to do this it must be added to /etc/fstab file

1. Run blkid utility to get the UUID

blkid

Copy the UUID

2. Add it to /etc/fstab

Open up your favourite editor and open the file, I’m using vim here:
vim /etc/fstab
Add the following line and replace with your UUID that you copied in the previous step:
UUID=<YOUR_UUID> /data ext4 defaults,nofail 1 2

3. Reboot the Virtual Machine and check that the mounted data disk is there:

reboot