Skip to main content

Command Palette

Search for a command to run...

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

Updated
5 min read
Creating a Linux VM Using SSH Keys with Terraform – Deploying an IaaS Web App
C

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

  1. Create a Project Directory: Open your terminal and run:

     tmkdir terraform-linux-vm
     cd terraform-linux-vm
    

  2. Initialize a Git Repository (Optional):

     git init
    

    This 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

  1. Validate the Configuration:

     terraform validate
    

  2. Initialize Terraform:

     terraform init -upgrade
    

  3. Plan the Deployment:

     terraform plan
    

    Review the plan to see which resources will be created

  4. Apply the Configuration:

     terraform apply -auto-approve
    

    This 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

  1. SSH into the VM:

     ssh -i "~/.ssh/id_rsa" azureuser@<public_ip>
    

    Replace <public_ip> with the IP address you retrieved.

  2. 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
    

  3. 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.

More from this blog

Everything Cloud Solution

49 posts