Deploy Ubuntu Cloud-Init with Azure VMs
Complete guide for deploying secure, production-ready Ubuntu virtual machines on Microsoft Azure using ARM templates and Cloud-Init automation

Written by RFS
Senior Penetration Tester / Security Researcher
Components Deployed:
- Azure Virtual Machine (Ubuntu 22.04 LTS)
- Virtual Network with Security Groups
- Public IP with DNS Label
- Managed Disk with Encryption
- Network Interface with Security Rules
Security Features:
- SSH Key Authentication Only
- UFW Firewall Configuration
- Fail2Ban Intrusion Prevention
- Automatic Security Updates
- Azure Monitor Integration
Required Tools:
- Azure CLI (version 2.0+)
- PowerShell (optional)
- Text editor (VS Code recommended)
- SSH key pair
Azure Requirements:
- Active Azure subscription
- Contributor role or higher
- Resource group permissions
- VM creation permissions
First, install Azure CLI and authenticate with your Azure account.
Install Azure CLI:
# Ubuntu/Debian
curl -sL https://aka.ms/InstallAzureCLIDeb | sudo bash
# macOS
brew install azure-cli
# Windows
winget install Microsoft.AzureCLI
Login to Azure:
# Login to Azure
az login
# Set your subscription (if you have multiple)
az account set --subscription "your-subscription-id"
# Verify your account
az account show
Create a resource group to contain all your Azure resources.
# Create resource group
az group create \
--name ubuntu-vm-rg \
--location eastus
# Verify resource group creation
az group show --name ubuntu-vm-rg
Create the main ARM template file that defines your Azure infrastructure.
azuredeploy.json:
{
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"vmName": {
"type": "string",
"defaultValue": "ubuntu-vm",
"metadata": {
"description": "Name of the virtual machine"
}
},
"adminUsername": {
"type": "string",
"defaultValue": "azureuser",
"metadata": {
"description": "Username for the Virtual Machine"
}
},
"sshPublicKey": {
"type": "string",
"metadata": {
"description": "SSH public key for authentication"
}
},
"vmSize": {
"type": "string",
"defaultValue": "Standard_B2s",
"allowedValues": [
"Standard_B1s",
"Standard_B2s",
"Standard_D2s_v3",
"Standard_D4s_v3"
],
"metadata": {
"description": "Size of the virtual machine"
}
}
},
"variables": {
"vnetName": "[concat(parameters('vmName'), '-vnet')]",
"subnetName": "default",
"networkSecurityGroupName": "[concat(parameters('vmName'), '-nsg')]",
"publicIPAddressName": "[concat(parameters('vmName'), '-pip')]",
"networkInterfaceName": "[concat(parameters('vmName'), '-nic')]",
"osDiskName": "[concat(parameters('vmName'), '-osdisk')]",
"addressPrefix": "10.0.0.0/16",
"subnetPrefix": "10.0.0.0/24",
"cloudInitData": "# Complete Cloud-Init configuration embedded here"
},
"resources": [
// Network Security Group, Virtual Network, Public IP, Network Interface, Virtual Machine
],
"outputs": {
"publicIPAddress": {
"type": "string",
"value": "[reference(resourceId('Microsoft.Network/publicIPAddresses', variables('publicIPAddressName'))).ipAddress]"
},
"fqdn": {
"type": "string",
"value": "[reference(resourceId('Microsoft.Network/publicIPAddresses', variables('publicIPAddressName'))).dnsSettings.fqdn]"
}
}
}
Create a parameters file to customize your deployment values.
azuredeploy.parameters.json:
{
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"vmName": {
"value": "my-ubuntu-vm"
},
"adminUsername": {
"value": "azureuser"
},
"sshPublicKey": {
"value": "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQC... # Your SSH public key here"
},
"vmSize": {
"value": "Standard_B2s"
},
"ubuntuOSVersion": {
"value": "22_04-lts-gen2"
}
}
}
Important
ssh-keygen -t rsa -b 4096
Deploy your ARM template to create the Azure VM with Cloud-Init configuration.
Deploy using Azure CLI:
# Deploy the ARM template
az deployment group create \
--resource-group ubuntu-vm-rg \
--template-file azuredeploy.json \
--parameters azuredeploy.parameters.json
# Monitor deployment status
az deployment group show \
--resource-group ubuntu-vm-rg \
--name azuredeploy \
--query "properties.provisioningState"
Alternative: Deploy using PowerShell:
# Deploy using PowerShell
New-AzResourceGroupDeployment \
-ResourceGroupName "ubuntu-vm-rg" \
-TemplateFile "azuredeploy.json" \
-TemplateParameterFile "azuredeploy.parameters.json"
# Get deployment outputs
(Get-AzResourceGroupDeployment -ResourceGroupName "ubuntu-vm-rg" -Name "azuredeploy").Outputs
Verify that your VM is running and connect via SSH to confirm the Cloud-Init configuration.
Get VM Information:
# Get VM details including public IP
az vm show \
--resource-group ubuntu-vm-rg \
--name my-ubuntu-vm \
--show-details \
--query "{Name:name, PowerState:powerState, PublicIP:publicIps, FQDN:fqdns}"
# Get deployment outputs
az deployment group show \
--resource-group ubuntu-vm-rg \
--name azuredeploy \
--query "properties.outputs"
Connect via SSH:
# Connect to your VM (replace with your FQDN)
ssh azureuser@your-vm-fqdn.eastus.cloudapp.azure.com
# Verify Cloud-Init completed successfully
sudo cloud-init status
# Check installed packages
dpkg -l | grep -E "(ufw|fail2ban|nginx)"
# Verify firewall status
sudo ufw status
# Check system logs
sudo journalctl -u cloud-init-local
sudo journalctl -u cloud-init
Security Checks:
# Check firewall status
sudo ufw status verbose
# Verify SSH configuration
sudo sshd -T | grep -E "(passwordauth|permitroot|pubkey)"
# Check Fail2Ban status
sudo fail2ban-client status
sudo fail2ban-client status sshd
# Verify system hardening
sudo sysctl net.ipv4.conf.all.accept_redirects
sudo sysctl net.ipv4.conf.all.accept_source_route
Service Status:
# Check service status
systemctl status nginx
systemctl status fail2ban
systemctl status ufw
# Verify automatic updates
sudo systemctl status unattended-upgrades
# Check Azure Monitor agent
sudo systemctl status azuremonitoragent
Deployment Fails with Authentication Error
Ensure your SSH public key is correctly formatted and doesn't contain line breaks. Use cat ~/.ssh/id_rsa.pub
to get the correct format.
Cloud-Init Fails to Complete
Check Cloud-Init logs: sudo cat /var/log/cloud-init-output.log
and sudo journalctl -u cloud-init
Cannot Connect via SSH
Verify the Network Security Group allows SSH (port 22) and check that your SSH key matches the one specified in the parameters file.
ARM Template Validation Errors
Use az deployment group validate
to check your template before deployment. Ensure all required parameters are provided.
What are ARM templates in Azure?
ARM (Azure Resource Manager) templates are JSON files that define the infrastructure and configuration for your Azure resources. They enable Infrastructure as Code (IaC) practices for consistent, repeatable deployments.
How does Cloud-Init work with Azure VMs?
Cloud-Init is automatically executed when an Azure VM boots for the first time. It reads the configuration data and performs system initialization tasks like package installation, user creation, and security hardening.
What are the security benefits of this deployment method?
This method provides automated security hardening including firewall configuration, SSH hardening, intrusion detection, system monitoring, and compliance with security best practices from the moment the VM starts.
Can I customize the ARM template for my specific needs?
Yes, the ARM template is fully customizable. You can modify VM sizes, networking configurations, storage options, and Cloud-Init scripts to match your specific requirements.
Infrastructure Enhancements:
- Set up Azure Load Balancer for high availability
- Configure Azure Backup for VM protection
- Implement Azure Key Vault for secrets management
- Set up Azure Monitor and Log Analytics
Security Improvements:
- Enable Azure Security Center
- Configure Azure Sentinel for SIEM
- Implement Azure Bastion for secure access
- Set up Azure Policy for compliance
Ready to Deploy Your Azure Infrastructure?
Generate custom Cloud-Init templates with our professional tool
Download Complete Project Files
Get all ARM templates, parameters, and Cloud-Init configurations in a single download