Back to Checks
azure

Virtual Machine uses an approved custom machine image

vm_ensure_using_approved_images
Service: vmSeverity: high
OfficialBuilt-inVerified
Categories:software-supply-chain
Resource Type:microsoft.compute/virtualmachines

Azure VMs are evaluated for use of an approved custom image by inspecting the VM image reference. The expected format is a subscription-scoped ID like /subscriptions/.../providers/Microsoft.Compute/images/<image>, not marketplace, gallery, or community sources.

Risk

Using unapproved images undermines integrity and confidentiality by introducing unknown packages, misconfigurations, or malware. Attackers can implant backdoors, weaken hardening, and bypass baselines, enabling data exfiltration and lateral movement, and harming availability with unpatched software.

Run this check with Prowler

Run in Prowler Cloud
Run
prowler azure --checks vm_ensure_using_approved_images

Recommendation

Standardize on golden images maintained in an Azure Compute Gallery or managed images.

  • Harden and patch each release; scan for vulnerabilities
  • Restrict who can create/publish images (least privilege)
  • Enforce deployments only from approved images via policy
  • Version, sign, and retire images regularly

Remediation

CLI

az vm create --resource-group <RESOURCE_GROUP> --name <VM_NAME> --image /subscriptions/<SUBSCRIPTION_ID>/resourceGroups/<RESOURCE_GROUP>/providers/Microsoft.Compute/images/<IMAGE_NAME> --admin-username azureuser --generate-ssh-keys

Terraform

# VM created from an approved custom managed image
resource "azurerm_windows_virtual_machine" "<example_resource_name>" {
  name                = "<example_resource_name>"
  resource_group_name = "<example_resource_id>"
  location            = "<example_resource_location>"
  size                = "Standard_DS1_v2"
  admin_username      = "<example_resource_name>"
  admin_password      = "<example_resource_id>"
  network_interface_ids = ["<example_resource_id>"]

  source_image_id = "/subscriptions/<subscription_id>/resourceGroups/<example_resource_id>/providers/Microsoft.Compute/images/<example_resource_name>" # CRITICAL: managed image ID ensures PASS

  os_disk {
    caching              = "ReadWrite"
    storage_account_type = "Standard_LRS"
  }
}

NativeIaC

// Create a VM using an approved custom managed image
param location string = resourceGroup().location
param nicId string
param adminUsername string
param adminPassword string

resource vm 'Microsoft.Compute/virtualMachines@2023-09-01' = {
  name: '<example_resource_name>'
  location: location
  properties: {
    hardwareProfile: { vmSize: 'Standard_DS1_v2' }
    storageProfile: {
      imageReference: {
        id: '/subscriptions/<subscription_id>/resourceGroups/<example_resource_id>/providers/Microsoft.Compute/images/<example_resource_name>' // CRITICAL: use managed image ID to pass check
      }
      osDisk: { createOption: 'FromImage' }
    }
    osProfile: {
      computerName: '<example_resource_name>'
      adminUsername: adminUsername
      adminPassword: adminPassword
    }
    networkProfile: {
      networkInterfaces: [{ id: nicId }]
    }
  }
}

Other

1In Azure Portal, go to Virtual machines > Create > Azure virtual machine
2Under Image, click See all images, then select the My Images tab
3Choose the approved managed image (type: Microsoft.Compute/images)
4Complete required basics and create the VM
5If replacing a non-compliant VM, migrate workload and delete the old VM

Check Code

from prowler.lib.check.models import Check, Check_Report_Azure
from prowler.providers.azure.services.vm.vm_client import vm_client


class vm_ensure_using_approved_images(Check):
    """
    Ensure that Azure VMs are using an approved (custom) machine image.

    This check evaluates whether Azure Virtual Machines are launched from an approved (custom) machine image by checking the image reference ID format.

    - PASS: The Azure VM is using an approved custom machine image.
    - FAIL: The Azure VM is not using an approved custom machine image.
    """

    def execute(self):
        findings = []
        for subscription_id, vms in vm_client.virtual_machines.items():
            subscription_name = vm_client.subscriptions.get(
                subscription_id, subscription_id
            )
            for vm in vms.values():
                report = Check_Report_Azure(metadata=self.metadata(), resource=vm)
                report.subscription = subscription_id
                image_id = getattr(vm, "image_reference", None)
                if (
                    image_id
                    and image_id.startswith("/subscriptions/")
                    and "/providers/Microsoft.Compute/images/" in image_id
                ):
                    report.status = "PASS"
                    report.status_extended = f"VM {vm.resource_name} in subscription {subscription_name} ({subscription_id}) is using an approved machine image: {image_id.split('/')[-1]}."
                else:
                    report.status = "FAIL"
                    report.status_extended = f"VM {vm.resource_name} in subscription {subscription_name} ({subscription_id}) is not using an approved machine image."
                findings.append(report)
        return findings