How can you create a Red Hat image for Azure based on an on premises Red Hat VM?

 

I had a university in the Midwest ask me how to create Red Hat Images for Azure from an on premises image. I put together a quick step by step using Azure ARM CLI.

 

Step 0 Prepare Red Hat On Premises VM image for Azure

 

It is very important to follow these pre-configuration steps here for Hyper-V or VMWare here to preconfigure and prepare the Red Hat 7.2 VM for successful image upload and usage in Azure.

 

In my example, I was running RHEL 7.2 in a Hyper-V 2012 R2 host. First, I ran the Hyper-V prep steps for Azure above. Next, I had to convert the native VHDX format to VHD format. I used the native Hyper-V conversion. I shutdown the RHEL VM and in the Hyper-V Manager I within the RHEL VM I picked ‘edit disk’:

 

image

Pick Next

 

image

Pick Convert

 

image

Pick VHD

 

You can also covert the VHDX to VHD using a tool here.

 

Step 1 Install Azure CLI if you haven’t already. Grab the Azure CLI bits here.

 

Step 2 Open command prompt, Terminal, or Bash, etc..

 

Step 3 Connect to Azure via CLI. I used the interactive login by typing:

 

Azure Login

 

Insert the Oauth Token into the web page and I was able to log into Azure with my Azure AD credentials. It should return an ‘OK’ if successful.

image

 

 

I ran Azure help to see the CLI version, etc..

 

image

 

For a full list of the CLI ARM commands see here.

 

Step 4Switch to ARM mode I ran config mode arm

 

image

 

View a list of available Azure CLI ARM commands here

 

Step 5 Prepare Azure for Red Hat 7.2 VHD upload

 

Create a Resource Group if needed

azure group create resourcegroupnamelocation

 

image

 

 

Create a storage group if needed

azure storage account create –l location –g resourcegroupname –type storagetype storageaccountname

 

image

 

Get your storage URI for the VHD upload

 

Step 6 Upload RedHat VHD to Azure

 

There are a few ways to accomplish this via Azure CLI or via Storage Explorer. Here is how to upload the RHEL VHD via the Azure CLI:

 

azure storage blob upload “ local path to RHEL vhd containername –t page –a storage account name –k storage account key

 

image

 

 

For more documentation on uploading via CLI see here.

Here is a VHD for CLI tool you can also leverage effectively if your on premises VM has dynamic disks. This tool will convert to fixed which is required for Azure VM uploads. 

 

Step 7 Create new Azure Red Hat VM based on Red Hat VHD image

 

azure group deployment create resourcegroupname deploymentname - -template-file path to local JSON file

 

Here is a sample JSON template I used:

{
"$schema": "
https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#" ,
"contentVersion": "1.0.0.0",
"parameters": {
"customVmName": {
"type": "string",
"metadata": {
"description": "This is the name of the your Red Hat VM"
}
},
"userImageStorageAccountName": {
"type": "string",
"metadata": {
"description": "This is the name of the your storage account of the RHEL vhd location"
}
},
"osDiskVhdUri": {
"type": "string",
"metadata": {
"description": "Uri path to the uploaded Red Hat VHD image"
}
},
"dnsLabelPrefix": {
"type": "string",
"metadata": {
"description": "DNS Label for the Public IP. Must be lowercase. It should match with the following regular expression: ^[a-z][a-z0-9-]{1,61}[a-z0-9]$ or it will raise an error."
}
},
"adminUserName": {
"type": "string",
"metadata": {
"description": "User Name for the Red Hat Virtual Machine"
}
},
"adminPassword": {
"type": "securestring",
"metadata": {
"description": "Password for the Red Hat Virtual Machine"
}
},
"osType": {
"type": "string",
"allowedValues": [
"Linux"
],
"metadata": {
"description": "Red Hat OS"
}
},
"vmSize": {
"type": "string",
"metadata": {
"description": "This is the size of your VM e.g. Standard_A9"
}
},
"newOrExistingVnet": {
"allowedValues": [ "new", "existing" ],
"type": "string",
"metadata": {
"description": "Select if this template needs a new VNet or will reference an existing VNet"
}
},
"newOrExistingVnetName": {
"type": "string",
"defaultValue": "",
"metadata": {
"description": "New or Existing VNet Name"
}
},
"newOrExistingSubnetName": {
"type": "string",
"defaultValue": "Subnet1",
"metadata": {
"description": "Subnet Name"
}
}
},
"variables": {
"publicIPAddressName": "userImagePublicIP",
"vmName": "[parameters('customVmName')]",
"nicName": "[concat(parameters('customVmName'),'Nic')]",
"publicIPAddressType": "Dynamic",
"apiVersion": "2015-06-15",
"templatelink": "[concat('
https://raw.githubusercontent.com/azure/azure-quickstart-templates/master/101-vm-from-user-image/',parameters('newOrExistingVnet'),'vnet.json')]"
  },
"resources": [
{
"apiVersion": "2015-01-01",
"name": "vnet-template",
"type": "Microsoft.Resources/deployments",
"properties": {
"mode": "incremental",
"templateLink": {
"uri": "[variables('templatelink')]",
"contentVersion": "1.0.0.0"
},
"parameters": {
"virtualNetworkName": {
"value": "[parameters('newOrExistingVnetName')]"
},
"subnetName": {
"value": "[parameters('newOrExistingSubnetName')]"
}
}
}
},
{
"apiVersion": "[variables('apiVersion')]",
"type": "Microsoft.Network/publicIPAddresses",
"name": "[variables('publicIPAddressName')]",
"location": "[resourceGroup().location]",
"properties": {
"publicIPAllocationMethod": "[variables('publicIPAddressType')]",
"dnsSettings": {
"domainNameLabel": "[parameters('dnsLabelPrefix')]"
}
}
},
{
"apiVersion": "2016-03-30",
"type": "Microsoft.Network/networkInterfaces",
"name": "[variables('nicName')]",
"location": "[resourceGroup().location]",
"dependsOn": [
"[concat('Microsoft.Network/publicIPAddresses/', variables('publicIPAddressName'))]",
"Microsoft.Resources/deployments/vnet-template"
],
"properties": {
"ipConfigurations": [
{
"name": "ipconfig1",
"properties": {
"privateIPAllocationMethod": "Dynamic",
"publicIPAddress": {
"id": "[resourceId('Microsoft.Network/publicIPAddresses',variables('publicIPAddressName'))]"
},
"subnet": {
"id": "[reference('vnet-template').outputs.subnet1Ref.value]"
}
}
}
]
}
},
{
"apiVersion": "[variables('apiVersion')]",
"type": "Microsoft.Compute/virtualMachines",
"name": "[variables('vmName')]",
"location": "[resourceGroup().location]",
"dependsOn": [
"[concat('Microsoft.Network/networkInterfaces/', variables('nicName'))]"
],
"properties": {
"hardwareProfile": {
"vmSize": "[parameters('vmSize')]"
},
"osProfile": {
"computerName": "[variables('vmName')]",
"adminUsername": "[parameters('adminUsername')]",
"adminPassword": "[parameters('adminPassword')]"
},
"storageProfile": {
"osDisk": {
"name": "[concat(variables('vmName'),'-osDisk')]",
"osType": "[parameters('osType')]",
"caching": "ReadWrite",
"createOption": "FromImage",
"image": {
"uri": "[parameters('osDiskVhdUri')]"
},
"vhd": {
"uri": "[concat(reference(concat('Microsoft.Storage/storageAccounts/', parameters('userImageStorageAccountName')), variables('apiVersion')).primaryEndpoints.blob, 'vhds/',variables('vmName'), uniquestring(resourceGroup().id), 'osDisk.vhd')]"
}
}
},
"networkProfile": {
"networkInterfaces": [
{
"id": "[resourceId('Microsoft.Network/networkInterfaces',variables('nicName'))]"
}
]
},
"diagnosticsProfile": {
"bootDiagnostics": {
"enabled": "true",
"storageUri": "[concat(reference(concat('Microsoft.Storage/storageAccounts/', parameters('userImageStorageAccountName')), variables('apiVersion')).primaryEndpoints.blob)]"
}
}
}
}
]
}

 

Sample of running the script with the JSON above:

image

 

For more on deploying JSON templates via CLI see here.

 

 Step 8 – Verify RHEL image worked properly

 

 Use SSH to connect to the RHEL VM running in Azure for verification:

 image