Creating a Linux VM Using SSH Keys with Terraform – Deploying an IaaS Web App

Hi there! 👋 I'm Daniel Ozoemena, a passionate Cloud Solution Architect and DevOps Engineer dedicated to building scalable, secure, and innovative cloud solutions. With hands-on experience in Azure, AWS, and Google Cloud Platform, I specialize in deploying infrastructure as code, automating workflows, and optimizing system reliability. Driven by a love for problem-solving, I constantly explore new technologies and best practices to deliver impactful results. Beyond the cloud, I enjoy mentoring, blogging about tech insights, and contributing to open-source projects. When I'm not automating deployments or creating secure virtual networks, you can find me playing chess, learning about AI, or brainstorming solutions to real-world challenges. Let’s connect and grow together on this tech journey! 🚀
In this blog post, I’ll guide you through the process of deploying a Linux virtual machine on Azure using Terraform, configuring it for secure SSH access, and finally deploying an IaaS web app (Apache web server hosting a cloned website) on that VM. This project demonstrates how to automate infrastructure provisioning while also integrating manual configuration tasks—all managed from the CLI and VS Code.
Prerequisites
Before you begin, make sure you have the following:
Azure Subscription: Active account on Azure.
Terraform Installed: Latest version of Terraform installed on your machine.
Azure CLI Installed: To interact with your Azure resources.
VS Code: For editing Terraform files.
SSH Key Pair: Pre-generated SSH keys (if not, we’ll generate one during the process).
Step 1: Create Your Terraform Project
Create a Project Directory: Open your terminal and run:
tmkdir terraform-linux-vm cd terraform-linux-vm
Initialize a Git Repository (Optional):
git initThis step helps you track changes to your Terraform code.
Step 2: Define Your Infrastructure in Terraform
Create a file called main.tf in your project directory. Paste the following configuration, which does the following:
Configures the Azure provider.
Creates a resource group.
Sets up a virtual network with a subnet.
Creates a Network Security Group (NSG) with rules.
Creates a public IP and network interface.
Deploys a Linux VM using an Ubuntu image and SSH keys.
Creates a public IP and network interface.
Deploys a Linux VM using an Ubuntu image and SSH keys.
hclCopyEditprovider "azurerm" {
features {}
subscription_id = "your-subscription"
}
resource "azurerm_resource_group" "my_rg" {
name = "my-terraform-rg"
location = "East US"
}
resource "azurerm_virtual_network" "my_vnet" {
name = "my-vnet"
location = azurerm_resource_group.my_rg.location
resource_group_name = azurerm_resource_group.my_rg.name
address_space = ["10.0.0.0/16"]
}
resource "azurerm_subnet" "my_subnet" {
name = "my-subnet"
resource_group_name = azurerm_resource_group.my_rg.name
virtual_network_name = azurerm_virtual_network.my_vnet.name
address_prefixes = ["10.0.1.0/24"]
}
resource "azurerm_network_security_group" "ssh_sg" {
name = "ssh-nsg"
location = azurerm_resource_group.my_rg.location
resource_group_name = azurerm_resource_group.my_rg.name
security_rule {
name = "allow-ssh"
priority = 100
direction = "Inbound"
access = "Allow"
protocol = "Tcp"
source_port_range = "*"
destination_port_range = "22"
source_address_prefix = "*"
destination_address_prefix = "*"
}
security_rule {
name = "allow-http"
priority = 110
direction = "Inbound"
access = "Allow"
protocol = "Tcp"
source_port_range = "*"
destination_port_range = "80"
source_address_prefix = "*"
destination_address_prefix = "*"
}
security_rule {
name = "allow-https"
priority = 120
direction = "Inbound"
access = "Allow"
protocol = "Tcp"
source_port_range = "*"
destination_port_range = "443"
source_address_prefix = "*"
destination_address_prefix = "*"
}
}
resource "azurerm_public_ip" "my_public_ip" {
name = "my-public-ip"
location = azurerm_resource_group.my_rg.location
resource_group_name = azurerm_resource_group.my_rg.name
allocation_method = "Static"
sku = "Basic"
}
resource "azurerm_network_interface" "my_nic" {
name = "my-nic"
location = azurerm_resource_group.my_rg.location
resource_group_name = azurerm_resource_group.my_rg.name
ip_configuration {
name = "my-ip-config"
subnet_id = azurerm_subnet.my_subnet.id
private_ip_address_allocation = "Dynamic"
public_ip_address_id = azurerm_public_ip.my_public_ip.id
}
}
resource "azurerm_linux_virtual_machine" "my_vm" {
name = "my-linux-vm"
resource_group_name = azurerm_resource_group.my_rg.name
location = azurerm_resource_group.my_rg.location
size = "Standard_B1s"
admin_username = "azureuser"
network_interface_ids = [
azurerm_network_interface.my_nic.id,
]
admin_ssh_key {
username = "azureuser"
public_key = file("~/.ssh/id_rsa.pub")
}
source_image_reference {
publisher = "Canonical"
offer = "UbuntuServer"
sku = "18.04-LTS"
version = "latest"
}
os_disk {
caching = "ReadWrite"
storage_account_type = "Standard_LRS"
}
tags = {
Environment = "Terraform"
}
}
You will be getting your Azure subscription ID by using this ‘‘az account list -o table
Step 3: Generate SSH Keys (if needed)
If you haven’t already created an SSH key pair, run the following in your terminal:
mkdir .ssh
ssh-keygen -t rsa

Step 4: Validate, Initialize, and Apply Terraform Configuration
Validate the Configuration:
terraform validate
Initialize Terraform:
terraform init -upgrade
Plan the Deployment:
terraform planReview the plan to see which resources will be created

Apply the Configuration:
terraform apply -auto-approveThis command will create your resource group, virtual network, subnet, NSG, public IP, NIC, and Linux VM.


Step 5: Retrieve the Public IP Address
Once the deployment is complete, you can view the public IP address of your VM:
az vm show -d -g my-terraform-rg -n my-linux-vm --query publicIps -o tsv

Step 6: Connect to the VM and Deploy the Web App
SSH into the VM:
ssh -i "~/.ssh/id_rsa" azureuser@<public_ip>Replace
<public_ip>with the IP address you retrieved.
Update and Install Apache: Once connected to the VM, run:
sudo apt update sudo apt upgrade -y sudo apt install apache2 -y sudo systemctl start apache2 sudo systemctl enable apache2

Deploy the IaaS Web App: Clone your website repository into Apache’s document root:
bashCopyEditcd /var/www/html/ sudo git clone https://github.com/yourtechie/babycare-website.git cd babycare-website sudo mv * /var/www/html/ sudo chown -R www-data:www-data /var/www/html/ sudo chmod -R 755 /var/www/html/ sudo systemctl restart apache2

Conclusion
This project demonstrates how to use Terraform to automate the provisioning of a complete Azure IaaS environment—including a Linux VM configured with SSH keys, network resources, and secure access settings. Once deployed, I installed Apache on the VM and cloned a web app repository to serve as a live demonstration of an IaaS web app. Using Terraform and CLI commands from VS Code not only streamlines the deployment process but also ensures that your infrastructure is reproducible and secure.
This end-to-end approach showcases my expertise in infrastructure as code, cloud automation, and secure web app deployment—skills that are highly valuable in modern cloud environments.




