Quickstart: Audit Azure security baseline for Linux with a test machine

Screen snippet of compliance report

In this guide, you will use Azure Policy to audit a test Virtual Machine (VM) against the Azure security baseline for Linux.

Specifically, you will:

  1. Create an empty resource group
  2. Assign the audit policy to the empty resource group
  3. Create a VM in the resource group and observe the audit results

Tip

Prerequisites

Before attempting the steps in this article:

  1. You will need an Azure account to create a resource group, policy assignment, and a virtual machine. If you don't have an Azure account, you can create a free trial.

  2. Choose how you will access your Azure environment, for example:

    1. EASY: Azure Cloud Shell at https://shell.azure.com
    2. OR: Use your own workstation and shell environment with Azure CLI installed and signed in
    3. OR: Use the Azure portal at https://portal.azure.com
  3. Ensure that you are signed in to your desired Azure test environment

    You can use az account show to see your current context. To sign in or change contexts, use az account login.


Step 1: Create a resource group

Tip

The use of "East US" (eastus) as an example location throughout this article is arbitrary. You can choose any available Azure location.

az group create --name my-demo-rg --location eastus

Step 2: Assign the policy to your empty test resource group

# Note this example is from a bash shell. Other shells may require different handling of special characters and variables
RG_ID=$(az group show --resource-group my-demo-rg --query id --output tsv)
az policy assignment create --policy "fc9b3da7-8347-4380-8e70-0a0361d8dedd" --display-name "Linux machines should meet requirements for the Azure compute security baseline" --scope "$RG_ID"

Step 3: Create a test VM and prepare it for Machine Configuration

  1. Create a Linux VM with a system assigned identity

    az vm create --name my-demo-vm-01 --resource-group my-demo-rg --image Ubuntu2204 --assign-identity [system] --size Standard_B2s
    

    Tip

    It is normal to receive an alert similar to "No access was given yet...". Azure Machine Configuration requires only that the machine have a managed identity, not any specific resource access.

  2. Install the Machine Configuration agent, as an Azure VM extension

    az vm extension set --resource-group my-demo-rg --vm-name my-demo-vm-01 --name ConfigurationForLinux --publisher Microsoft.GuestConfiguration --enable-auto-upgrade
    

Tip

The managed identity and Machine Configuration extension steps were performed manually in this guide to reduce waiting and to reduce context changes. At scale, these can be satisfied using the Deploy prerequisites to enable Guest Configuration policies on virtual machines built-in policy initiative.

Step 4: Take a break before proceeding

Several steps will now happen automatically. Each of these steps can take a few minutes. Accordingly, please wait at least 15 minutes before proceeding.

Step 5: Observe results

The following examples show how to get:

  1. Count of machines by compliance state (useful at production scales, where you might have thousands of machines)
  2. List of machines with compliance state for each
  3. Detailed list of baseline rules with compliance state and evidence (also known as Reasons) for each

Tip

Expect to see red non-compliant results in the following. The audit-only use case is about discovering the difference between existing systems and the Azure security baseline.

The following Azure CLI examples are from a bash environment. To use another shell environment, you might need to adjust examples for line ending behavior, quote rules, character escaping, and so on.

  1. Count of machines by compliance state:

    QUERY='
    // Returns one record per observed compliance status bucket, with machine counts for each
    guestconfigurationresources
    | where name contains "AzureLinuxBaseline"
    | extend ["> ComplianceStatus"] = tostring(properties.complianceStatus)
    | summarize MachineCount = count() by ["> ComplianceStatus"]'
    az graph query --graph-query "$QUERY" --query data --output yamlc
    
  2. List of machines with compliance state for each:

    QUERY='
    // returns one record per machine, with status
    guestconfigurationresources
    | where name contains "AzureLinuxBaseline"
    | project ["> Machine"] = split(properties.targetResourceId,"/")[-1],
      ComplianceStatus = properties.complianceStatus,
      LastComplianceCheck = properties.lastComplianceStatusChecked'
    az graph query --graph-query "$QUERY" --query data --output yamlc
    
  3. Detailed list of baseline rules with compliance state and evidence (also known as Reasons) for each:

    QUERY='
    // Returns one record per baseline rule (around 200 per machine) with status for each
    GuestConfigurationResources
    | where name contains "AzureLinuxBaseline"
    | project Report = properties.latestAssignmentReport,
      Machine = split(properties.targetResourceId,"/")[-1],
      LastComplianceCheck=properties.lastComplianceStatusChecked
    | mv-expand Report.resources
    | project ["> Machine"] = Machine,
      ["> Rule"] = Report_resources.resourceId,
      RuleComplianceStatus = Report_resources.complianceStatus,
      RuleComplianceReason = Report_resources.reasons[0].phrase,
      LastComplianceCheck'
    
    az graph query --graph-query "$QUERY" --query data --output yamlc
    

The video is included to give an overall sense of workflow. For query details, rely on the text in this article.

Optional: Add more test machines to experience scale

In this article, the policy was assigned to a resource group which was initially empty and then gained one VM. While this demonstrates the system working end-to-end, it doesn't provide a sense of at-scale operations with lists of machines and so on.

Consider adding more test machines to the resource group, whether manually or via automation. These machines could be Azure VMs or Arc-enabled machines. As you see those machines come into compliance (or even fail), you can gain a keener sense of operationalizing Azure security baseline at scale.

Clean up resources

To avoid ongoing charges, consider deleting the resource group used in this article. For example, the Azure CLI command would be az group delete --name "my-demo-rg".