Using Inspec-Azure to test your Azure Resources

Inspec-Azure is a resource pack provided by Chef that uses the Azure REST API, to allow you to write tests for resources that you have deployed in Microsoft Azure. These tests can be used to validate the Azures resources that were deployed via code using Terraform or Azure RM templates. Inspec is an open source framework that is used for testing and auditing your infrastructure, in this blog post I will show how you can create tests against your Azure resources using Inspec-Azure.

Why validate my deployment?

When you deploy your Azure Resources whether via the Portal or code using a CI/CD pipeline; the deployment will follow some-sort of approval process and deployed with a specific set of requirements in mind. The deployment may deploy OK and look fine but the deployment has not been validated as what is expected to be deployed and the actual state the specific resources are in. This is where a validation stage is recommended; this stage will run tests you created to test and validate your Azure resources to confirm they are in the desired end-state.

There is where Inspec-Azure will come into play; as mentioned Inspec is an open source framework that is used for testing and auditing your infrastructure, Inspec-Azure sits on top of Inspec and uses the Azure REST API to query the resources that have been deployed.

How do the tests work?

The tests you create are self-explanatory, ruby-based and easy to read. Inspec-Azure works by comparing the actual state of your Azure resources with the desired state that you have expressed in your tests, if violations are detected between the comparing these are outputted in the form of a report, but you are in control of any remediation, can review the report and determine what to do next with the Azure resource to make it compliant again against the failed test.

So in theory, you create tests against your Azure Resources, these are ran against the current state of what is deployed and then an output is reviewed to determine if the Azure resources are in compliance and are the configurations are exactly in the state you want them to be.

Sounds awesome doesn’t it? I have gave theory behind why validating your deployments is recommended and how this can be done in Azure using Inspec-Azure, lets get to testing this out!

Before you run some tests; review the prerequisites

Once the above is setup, we are ready to go (Note:- in this example I am using Windows)

Deploy Azure Resources via Terraform

I will deploy:-

Azure Resource Group:- tamops
Azure Virtual Network: tamops-vnet
Azure Subnet inside Virtual Network: subnet

provider "azurerm" {
    # The "feature" block is required for AzureRM provider 2.x.
    # If you're using version 1.x, the "features" block is not allowed.
    version = "~>2.0"
    features {}
}

data "azurerm_client_config" "current" {}

#Create Resource Group
resource "azurerm_resource_group" "tamops" {
  name     = "tamops"
  location = "eastus2"
}

#Create Virtual Network
resource "azurerm_virtual_network" "vnet" {
    name                = "tamops-vnet"
    address_space       = ["192.168.0.0/16"]
    location            = "eastus2"
    resource_group_name = azurerm_resource_group.tamops.name
}

# Create Subnet
resource "azurerm_subnet" "subnet" {
  name                      = "subnet"
  resource_group_name       = azurerm_resource_group.tamops.name
  virtual_network_name      = azurerm_virtual_network.vnet.name
  address_prefix            = "192.168.0.0/24"
}

Time to run Inspec-Azure

Now that you have deployed the required resources, lets look at creating tests for the newly deployed Azure Resources.

Firstly, we need to create a new Inspec profile:-

inspec init profile --platform azure azure-inspec-tests

Successful output:-

 ─────────────────────────── InSpec Code Generator ───────────────────────────

Creating new profile at C:/Users/thomast/Documents/blog/Inspec-Azure-Local-Example/azure-inspec-tests
 • Creating directory controls
 • Creating file controls/example.rb
 • Creating file inspec.yml
 • Creating file README.md

Reviewing inspec.yml, you will see the references to Azure and reference to inspec-azure along with the url to download.

name: azure-inspec-tests
title: Azure InSpec Profile
maintainer: The Authors
copyright: The Authors
copyright_email: you@example.com
license: Apache-2.0
summary: An InSpec Compliance Profile For Azure
version: 0.1.0
inspec_version: '>= 2.2.7'
depends:
- name: inspec-azure
  url: https://github.com/inspec/inspec-azure/archive/master.tar.gz
supports:
- platform: azure

Notice the controls folder? This is where you create the ruby tests to run against your Azure resources. I have created two examples below:-

  • azure_resource_group.rb
  • azure_virtual_network.rb

azure_resource_group.rb will check that the resource group exists

resource_group = 'tamops'

control 'azurerm_resource_groups' do
  describe azurerm_resource_groups.where(name: resource_group) do
    it               { should exist }
  end
end

azure_virtual_network.rb will check that the virtual network is in the required resource group, with the address space: 192.168.0.0/16 and subnet has been deployed.

resource_group = 'tamops'
virtual_network = 'tamops-vnet'
address_space   = '192.168.0.0/16'
subnet1         = 'subnet'


describe azurerm_virtual_network(resource_group: resource_group, name: virtual_network) do
    its('address_space') { should eq [address_space] }
    its('subnets') { should eq [subnet1] }

end

Run inspec-azure against your Azure Resources to test

inspec exec azure-inspec-tests -t azure://

Succesful Output:-

PS C:\Users\thomast\Documents\blog\Inspec-Azure-Local-Example> inspec exec azure-inspec-tests -t azure://

Profile: Azure InSpec Profile (azure-inspec-tests)
Version: 0.1.0
Target:  azure://xxxxxxxxxxxxxxxx

  [PASS]  azurerm_resource_groups: Resource Groups with name == "tamops"
     [PASS]  Resource Groups with name == "tamops" is expected to exist

  'tamops-vnet' Virtual Network
     [PASS]  address_space is expected to eq ["192.168.0.0/16"]
     [PASS]  subnets is expected to eq ["subnet"]

Profile: Azure Resource Pack (inspec-azure)
Version: 1.19.5
Target:  azure://xxxxxxxxxxxxxxxx

     No tests executed.

Profile Summary: 1 successful control, 0 control failures, 0 controls skipped
Test Summary: 3 successful, 0 failures, 0 skipped

Awesome, the tests have passed!

What does a fail test look like? Lets change resource_group = ‘tamops‘ to resource_group = ‘tamopsfail‘ in azurerm_resource_group.rb and re-run.

Output showing a failed test:-

PS C:\Users\thomast\Documents\blog\Inspec-Azure-Local-Example> inspec exec azure-inspec-tests -t azure://

Profile: Azure InSpec Profile (azure-inspec-tests)
Version: 0.1.0
Target:  azure://xxxxxxxxxxxxxxxx

  [FAIL]  azurerm_resource_groups: Resource Groups with name == "tamopsfail"
     [FAIL]  Resource Groups with name == "tamopsfail" is expected to exist
     expected Resource Groups with name == "tamopsfail" to exist

  'tamops-vnet' Virtual Network
     [PASS]  address_space is expected to eq ["192.168.0.0/16"]
     [PASS]  subnets is expected to eq ["subnet"]

Profile: Azure Resource Pack (inspec-azure)
Version: 1.19.5
Target:  azure://xxxxxxxxxxxxxxxx

     No tests executed.

Profile Summary: 0 successful controls, 1 control failure, 0 controls skipped
Test Summary: 2 successful, 1 failure, 0 skipped

I have now showed how you can create tests against your Azure resources using Inspec-Azure, in this blog I showed how it can be ran locally. In a future blog I will show how it can included as part of an Azure DevOps Pipeline in relation to CI/CD deployment.

Note:- In the latest version of Inspec-Azure there is only a certain amount of resources that are available to be tested against, in previous versions of Inspec-Azure you can use azure_generic_resource but it is deprecated in newer versions. In a later blog I will show how this can be referenced and used within a Docker container.

Additional related blog posts I recommend

Cheat Sheet – InSpec
Azure Platform Support in Inspec
Available Azure Resources that can be tested using Inspec-Azure

Code used in this blog post has been uploaded to Github

2 comments

Leave a Reply