3. Create Linux virtual machine using Azure CLI

In this section of the tutorial, use the Azure CLI to create and configure your virtual machine. At this point in the tutorial, you should have a terminal window open and signed into the Azure cloud on the subscription where you intend to create the virtual machine.

All of the Azure CLI steps can be completed from a single instance of the Azure CLI. If you close the window or switch where you are using Azure CLI, such as between the Cloud Shell and your local terminal, you will need to sign in again.

Create a cloud-init file to expedite linux virtual machine creation

This tutorial uses a cloud-init configuration file to create both the NGINX reverse proxy server and the Express.js server. NGINX is used to forward the Express.js port (3000) to the public port (80).

  1. Create a local file named cloud-init-github.txt and save the following contents to the file or you can save the repository's file to your local computer. The cloud-init formatted file needs to exist in the same folder as the terminal path for your Azure CLI commands.

    #cloud-config
    package_upgrade: true
    packages:
      - nginx
    write_files:
      - owner: www-data:www-data
        path: /etc/nginx/sites-available/default
        content: |
          server {
            listen 80 default_server;
            server_name _;
            location / {
              # First, try if the file exists locally, otherwise request it from the app
              try_files $uri @app;
            }
            location @app {
              proxy_pass http://localhost:3000;
              proxy_http_version 1.1;
              proxy_set_header Upgrade $http_upgrade;
              proxy_set_header Connection 'upgrade';
              proxy_set_header X-Forwarded-For $remote_addr;
              proxy_set_header Host $host;
              proxy_cache_bypass $http_upgrade;
            }
          }
    runcmd:
      # install Node.js
      - 'curl -sL https://deb.nodesource.com/setup_16.x | sudo -E bash -'
      - 'sudo apt-get install -y nodejs'
      # clone GitHub Repo into myapp directory
      - 'cd /home/azureuser'
      - git clone "https://github.com/Azure-Samples/js-e2e-vm" myapp
      # Start app
      - 'cd myapp && npm install && npm start'
      # restart NGINX
      - systemctl restart nginx
    
  2. Review the runcmd section of file to understand what it does.

    The runcmd has several tasks:

    • Download Node.js, and install it
    • Clone the sample Express.js repository from GitHub into myapp directory
    • Install the application dependencies
    • Start the Express.js app with PM2

Create a virtual machine resource

  1. Enter the Azure CLI command, az vm create, at a terminal to create an Azure resource of a Linux virtual machine. The command creates the VM from the cloud-init file and generates the SSH keys for you. The running command displays where the keys are stored.

    az vm create \
      --resource-group rg-demo-vm-eastus \
      --name demo-vm \
      --location eastus \
      --public-ip-sku Standard \
      --image UbuntuLTS \
      --admin-username azureuser \
      --generate-ssh-keys \
      --custom-data cloud-init-github.txt
    

    The process may take a few minutes.

  2. Keep the publicIpAddress value from the response, it is needed to view the web app in a browser and to connect to the VM.

Open port for virtual machine

When first created, the virtual machine has no open ports. Open port 80 with the following Azure CLI command, az vm open-port so the web app is publicly available:

az vm open-port \
  --port 80 \
  --resource-group rg-demo-vm-eastus \
  --name demo-vm

Browse to web site

  1. Use the public IP address in a web browser to make sure the virtual machine is available and running. Change the URL to use the value from publicIpAddress.

    http://YOUR-VM-PUBLIC-IP-ADDRESS
    
  2. The virtual machine's web app returns the following information:

    • VM name
    • Your client IP
    • Current Date/Time

    Simple app served from Linus virtual machine on Azure.

  3. If the resource fails with a gateway error, try again in a minute, the web app may take a minute to start.

  4. The initial code file for the web app has a single route, which passed through the NGINX proxy.

    const os = require('os');
    const express = require('express')
    const app = express()
    
    app.use('/public', express.static('public'))
    app.get('/', function (req, res) {
    
        const clientIP = req.headers['x-forwarded-for'];
        const msg = `HostName: ${os.hostname()}<br>ClientIP: ${clientIP}<br>DateTime: ${new Date()}<br><img width='200' height='200' src='/public/leaves.jpg' alt='flowers'>`
        console.log(msg)
    
        res.send(msg)
    })
    app.listen(3000, function () {
        console.log(`Hello world app listening on port 3000! ${Date.now()}`)
    })
    

Next step