/inspec-azure

InSpec Azure Resource Pack

Primary LanguageRubyOtherNOASSERTION

InSpec for Azure

  • Project State: Maintained

For more information on project states and SLAs, see this documentation.

Build Status

This InSpec resource pack uses the Azure REST API and provides the required resources to write tests for resources in Azure.

Table of Contents

Prerequisites

  • Ruby
  • Bundler installed
  • Azure Service Principal Account

Service Principal

Your Azure Service Principal Account must have a minimum of reader role of the Azure roles to any subscription that you'd like to use this resource pack against.

You should have the following pieces of information:

  • TENANT_ID
  • CLIENT_ID
  • CLIENT_SECRET
  • SUBSCRIPTION_ID

To create your account Service Principal Account:

  1. Login to the Azure portal.
  2. Click on Azure Active Directory.
  3. Click on APP registrations.
  4. Click on New application registration.
  5. Fill in a name and select Web from the Application Type drop down. Save your application.
  6. Note your Application ID. This is your client_id above.
  7. Click on Certificates & secrets.
  8. Click on New client secret.
  9. Create a new password. This value is your client_secret above.
  10. Go to your subscription (click on All Services then subscriptions). Choose your subscription from that list.
  11. Note your Subscription ID can be found here.
  12. Click Access control (IAM).
  13. Click Add.
  14. Select the reader role.
  15. Select the application you just created and save.

These must be stored in a environment variables prefaced with AZURE_. If you use Dotenv, then you may save these values in your own .envrc file. Either source it or run direnv allow. If you don't use Dotenv, then you may just create environment variables in the way that your prefer.

Use the Resources

Since this is an InSpec resource pack, it only defines InSpec resources. To use these resources in your own controls you should create your own profile:

Create a new profile

$ inspec init profile --platform azure my-profile

Example inspec.yml:

name: my-profile
title: My own Azure profile
version: 0.1.0
inspec_version: '>= 4.23.15'
depends:
  - name: inspec-azure
    url: https://github.com/inspec/inspec-azure/archive/x.tar.gz
supports:
  - platform: azure

(For available inspec-azure versions, see this list of inspec-azure versions.)

Resource Documentation

The following is a list of generic resources.

With the generic resources:

  • Azure cloud resources that this resource pack does not include a static InSpec resource for can be tested.
  • Azure resources from different resource providers and resource groups can be tested at the same time.
  • Server side filtering can be used for more efficient tests.

The following is a list of static resources.

For more details and different use cases, please refer to the specific resource pages.

Examples

Interrogate All Resources that Have project_A in Their Names within Your Subscription Regardless of Their Type and Resource Group

azure_generic_resources(substring_of_name: 'project_A').ids.each do |id|
  describe azure_generic_resource(resource_id: id) do
    its('location') { should eq 'eastus' }
  end
end

Interrogate All Resources that Have a Tag Defined with the Name project_A Regardless of its Value

azure_generic_resources(tag_name: 'project_A').ids.each do |id|
  describe azure_generic_resource(resource_id: id) do
    its('location') { should eq 'eastus' }
  end
end

Verify Properties of an Azure Virtual Machine

describe azure_virtual_machine(resource_group: 'MyResourceGroup', name: 'prod-web-01') do
  it { should exist }
  it { should have_monitoring_agent_installed }
  it { should_not have_endpoint_protection_installed([]) }
  it { should have_only_approved_extensions(['MicrosoftMonitoringAgent']) }
  its('type') { should eq 'Microsoft.Compute/virtualMachines' }
  its('installed_extensions_types') { should include('MicrosoftMonitoringAgent') }
  its('installed_extensions_names') { should include('LogAnalytics') }
end

Verify Properties of a Network Security Group

describe azure_network_security_group(resource_group: 'ProductionResourceGroup', name: 'ProdServers') do
  it { should exist }
  its('type') { should eq 'Microsoft.Network/networkSecurityGroups' }
  its('security_rules') { should_not be_empty }
  its('default_security_rules') { should_not be_empty }
  it { should_not allow_rdp_from_internet }
  it { should_not allow_ssh_from_internet }
  it { should allow(source_ip_range: '0.0.0.0', destination_port: '22', direction: 'inbound') }
  it { should allow_in(service_tag: 'Internet', port: %w{1433-1434 1521 4300-4350 5000-6000}) }
end

Parameters Applicable To All Resources

The generic resources and their derivations support following parameters unless stated otherwise in their specific resource page.

api_version

As an Azure resource provider enables new features, it releases a new version of the REST API. They are generally in the format of 2020-01-01. InSpec Azure resources can be forced to use a specific version of the API to eliminate the behavioural changes between the tests using different API versions. The latest version will be used unless a specific version is provided.

User Provided Api Version

describe azure_virtual_machine(resource_group: 'my_group', name: 'my_VM', api_version: '2020-01-01') do
  its('api_version_used_for_query_state') { should eq 'user_provided' }
  its('api_version_used_for_query') { should eq '2020-01-01' }
end

Pre-defined Default Api Version

default api version can be used if it is supported by the resource provider.

describe azure_generic_resource(resource_provider: 'Microsoft.Compute/virtualMachines', name: 'my_VM', api_version: 'default') do
  its('api_version_used_for_query_state') { should eq 'default' }
end

Latest Api Version

latest version will be determined by this resource pack within the supported api versions. If the latest version is a preview than an older but a stable version might be used. Explicitly forcing to use the latest version.

describe azure_virtual_networks(api_version: 'latest') do
  its('api_version_used_for_query_state') { should eq 'latest' }
end

latest version will be used unless provided (Implicit).

describe azure_network_security_groups(resource_group: 'my_group') do
  its('api_version_used_for_query_state') { should eq 'latest' }
end

latest version will be used if the provided is invalid.

describe azure_network_security_groups(resource_group: 'my_group', api_version: 'invalid_api_version') do
  its('api_version_used_for_query_state') { should eq 'latest' }
end

endpoint

Microsoft Azure cloud services are available through a global and three national network of datacenter as described here. The preferred data center can be defined via endpoint parameter. Azure Global Cloud will be used if not provided.

  • azure_cloud (default)
  • azure_china_cloud
  • azure_us_government_L4
  • azure_us_government_L5
  • azure_german_cloud
describe azure_virtual_machines(endpoint: 'azure_german_cloud') do
  it { should exist }
end

It can be defined as an environment variable or a resource parameter (has priority).

The predefined environment variables for each cloud deployments can be found here.

http_client parameters

The behavior of the http client can be defined with the following parameters:

  • azure_retry_limit: Maximum number of retries (default - 2, Integer).
  • azure_retry_backoff: Pause in seconds between retries (default - 0, Integer).
  • azure_retry_backoff_factor: The amount to multiply each successive retries interval amount by (default - 1, Integer).

They can be defined as environment variables or resource parameters (has priority).


WARNING The following resources are using their azure_ counterparts under the hood and they will be deprecated in the InSpec Azure version 2. Their api versions are fixed (see below) for full backward compatibility. It is strongly advised to start using the resources with azure_ prefix for an up-to-date testing experience.

Legacy Resource Name Fixed api version Replaced by
azurerm_ad_user, azurerm_ad_users v1.0 azure_graph_user, azure_graph_users
azurerm_aks_cluster, azurerm_aks_clusters 2018-03-31 azure_aks_cluster, azure_aks_cluster
azurerm_api_management, azurerm_api_managements 2019-12-01 azure_api_management, azure_api_managements
azurerm_application_gateway, azurerm_application_gateways 2019-12-01 azure_application_gateway, azure_application_gateways
azurerm_cosmosdb_database_account 2015-04-08 azure_cosmosdb_database_account
azurerm_event_hub_authorization_rule 2017-04-01 azure_event_hub_authorization_rule
azurerm_event_hub_event_hub 2017-04-01 azure_event_hub_event_hub
azurerm_event_hub_namespace 2017-04-01 azure_event_hub_namespace
azurerm_hdinsight_cluster 2015-03-01-preview azure_hdinsight_cluster
azurerm_iothub 2018-04-01 azure_iothub
azurerm_iothub_event_hub_consumer_group, azurerm_iothub_event_hub_consumer_groups 2018-04-01 azure_iothub_event_hub_consumer_group, azure_iothub_event_hub_consumer_groups
azurerm_key_vault, azurerm_key_vaults 2016-10-01 azure_key_vault, azure_key_vaults
azurerm_key_vault_key, azurerm_key_vault_keys 2016-10-01 azure_key_vault_key, azure_key_vault_keys
azurerm_key_vault_secret, azurerm_key_vault_secrets 2016-10-01 azure_key_vault_secret, azure_key_vault_secrets
azurerm_load_balancer, azurerm_load_balancers 2018-11-01 azure_load_balancer, azure_load_balancers
azurerm_locks 2016-09-01 azure_locks
azurerm_management_group, azurerm_management_groups 2018-03-01-preview azure_management_group, azure_management_groups
azurerm_mariadb_server, azurerm_mariadb_servers 2018-06-01-preview azure_mariadb_server, azure_mariadb_servers
azurerm_monitor_activity_log_alert, azurerm_monitor_activity_log_alerts 2017-04-01 azure_monitor_activity_log_alert, azure_monitor_activity_log_alerts
azurerm_monitor_log_profile, azurerm_monitor_log_profiles 2016-03-01 azure_monitor_log_profile, azure_monitor_log_profiles
azurerm_mysql_database, azurerm_mysql_databases 2017-12-01 azure_mysql_database, azure_mysql_databases
azurerm_mysql_server, azurerm_mysql_servers 2017-12-01 azure_mysql_server, azure_mysql_servers
azurerm_network_interface, azurerm_network_interfaces 2018-11-01 azure_network_interface, azure_network_interfaces
azurerm_network_security_group, azurerm_network_security_groups 2018-02-01 azure_network_security_group, azure_network_security_groups
azurerm_network_watcher, azurerm_network_watchers 2018-02-01 azure_network_watcher, azure_network_watchers
azurerm_postgresql_database, azurerm_postgresql_databases 2017-12-01 azure_postgresql_database, azure_postgresql_databases
azurerm_postgresql_server, azurerm_postgresql_servers 2017-12-01 azure_postgresql_server, azure_postgresql_servers
azurerm_public_ip 2020-05-01 azure_public_ip
azurerm_resource_groups 2018-02-01 azure_resource_groups
azurerm_role_definition, azurerm_role_definitions 2015-07-01 azure_role_definition, azure_role_definitions
azurerm_security_center_policy, azurerm_security_center_policies 2015-06-01-Preview azure_security_center_policy, azure_security_center_policies
azurerm_sql_database, azurerm_sql_databases 2017-10-01-preview azure_sql_database, azure_sql_databases
azurerm_sql_server, azurerm_sql_servers 2018-06-01-preview azure_sql_server, azure_sql_servers
azurerm_storage_account, azurerm_storage_accounts 2017-06-01 azure_storage_account, azure_storage_accounts
azurerm_storage_account_blob_container, azurerm_storage_account_blob_containers 2018-07-01 azure_storage_account_blob_container, azure_storage_account_blob_containers
azurerm_subnet, azurerm_subnets 2018-02-01 azure_subnet, azure_subnets
azurerm_subscription 2019-10-01 azure_subscription
azurerm_virtual_machine, azurerm_virtual_machines 2017-12-01 azure_virtual_machine, azure_virtual_machines
azurerm_virtual_machine_disk, azurerm_virtual_machine_disks 2017-03-30 azure_virtual_machine_disk, azure_virtual_machine_disks
azurerm_virtual_network, azurerm_virtual_networks 2018-02-01 azure_virtual_network, azure_virtual_networks
azurerm_webapp, azurerm_webapps 2016-08-01 azure_webapp, azure_webapps

Development

If you'd like to contribute to this project please see Contributing Rules.

For a detailed walk-through of resource creation, see the Resource Creation Guide.

Developing a Static Resource

The static resource is an InSpec Azure resource that is used to interrogate a specific Azure resource, such as, azure_virtual_machine, azure_key_vaults. As opposed to the generic resources, they might have some static properties created by processing the dynamic properties of a resource, such as, azure_virtual_machine.admin_username.

The easiest way to start is checking the existing static resources. They have detailed information on how to leverage the backend class within their comments.

The common parameters are:

  • resource_provider: Such as Microsoft.Compute/virtualMachines. It has to be hardcoded in the code by the resource author via the specific_resource_constraint method, and it should be the first parameter defined in the resource. This method includes user-supplied input validation.
  • display_name: A generic one will be created unless defined.
  • required_parameters: Define mandatory parameters. The resource_group and resource name in the singular resources are default mandatory in the base class.
  • allowed_parameters: Define optional parameters. The resource_group is optional in plural resources, but this can be made mandatory in the static resource.
  • resource_uri: Azure REST API URI of a resource. This parameter should be used when a resource does not reside in a resource group. It requires add_subscription_id to be set to either true or false. See azure_policy_definition and azure_policy_definitions.
  • add_subscription_id: It indicates whether the subscription ID should be included in the resource_uri or not.

Singular Resources

The singular resource is used to test a specific resource of a specific type and should include all of the properties available, such as, azure_virtual_machine.

  • In most cases resource_group and resource name should be required from the users and a single API call would be enough for creating methods on the resource. See azure_virtual_machine for a standard singular resource and how to create static methods from resource properties.
  • If it is beneficial to accept the resource name with a more specific keyword, such as server_name, see azure_mysql_server.
  • If a resource exists in another resource, such as a subnet on a virtual network, see azure_subnet.
  • If it is necessary to make an additional API call within a static method, the create_additional_properties should be used. See azure_key_vault.

Plural Resources

A plural resource is used to test the collection of resources of a specific type, such as, azure_virtual_machines. This allows for tests to be written based on the group of resources.

  • A standard plural resource does not require a parameter, except optional resource_group. See azure_mysql_servers.
  • All plural resources use FilterTable to be able to provide filtering within returned resources. The filter criteria must be defined table_schema Hash variable.
  • If the properties of the resource are to be manipulated before populating the FilterTable, a populate_table method has to be defined. See azure_virtual_machines.
  • If the resources exist in another resource, such as subnets of a virtual network, a resource_path has to be created. For that, the identifiers of the parent resource, resource_group and virtual network name vnet, must be required from the users. See azure_subnets.

Setting the Environment Variables

The following instructions will help you get your development environment setup to run integration tests.

Copy .envrc-example to .envrc and fill in the fields with the values from your account.

export AZURE_SUBSCRIPTION_ID=<subscription id>
export AZURE_CLIENT_ID=<client id>
export AZURE_TENANT_ID=<tenant id>
export AZURE_CLIENT_SECRET=<client secret>

For PowerShell, set the following environment variables

$env:AZURE_SUBSCRIPTION_ID="<subscription id>"
$env:AZURE_CLIENT_ID="<client id>"
$env:AZURE_CLIENT_SECRET="<client secret>"
$env:AZURE_TENANT_ID="<tenant id>"

Setup Azure CLI

  • Follow the instructions for your platform here
    • macOS: brew update && brew install azure-cli
  • Login with the azure-cli
    • rake azure:login
  • Verify azure-cli is logged in:
    • az account show

Starting an Environment

First ensure your system has Terraform installed.

This environment may be used to run your profile against or to run integration tests on it. We are using Terraform workspaces to allow for teams to have completely unique environments without affecting each other.

Direnv

Direnv is used to initialize an environment variable WORKSPACE to your username. We recommend using direnv and allowing it to run in your environment. However, if you prefer to not use direnv you may also source .envrc.

Rake Commands

Creating a new environment:

rake azure:login
rake tf:apply

Updating a running environment (e.g. when you change the .tf file):

rake tf:apply

Checking if your state has diverged from your plan:

rake tf:plan

Destroying your environment:

rake tf:destroy

To run Rubocop and Syntax check for Ruby and InSpec:

rake test:lint

To run unit tests:

rake test:unit

To run integration tests:

rake test:integration

Please note that Graph API resource requires specific privileges granted to your service principal. Please refer to the Microsoft Documentation for information on how to grant these permissions to your application.

To run a control called azure_virtual_machine only:

rake test:integration[azurerm_virtual_machine]

Note that in zsh you need to escape the [, ] characters.

You may run selected multiple controls only:

rake test:integration[azure_aks_cluster,azure_virtual_machine]

To run lint and unit tests:

rake

Optional Components

The creation of the following resources can be skipped if there is any resource constraints.

  • Network Watcher
rake tf:apply[network_watcher]
  • HDinsight Interactive Query Cluster
rake tf:apply[hdinsight_cluster]
  • Public IP
rake tf:apply[public_ip]
  • API Management
rake tf:apply[api_management]
  • Management Group
rake tf:apply[management_group]

A combination of the above can be provided.

rake tf:apply[management_group,public_ip,network_watcher]