Skip to content

Commit

Permalink
Add azure_lock(s) resources (#316)
Browse files Browse the repository at this point in the history
Signed-off-by: Omer Demirok <[email protected]>
  • Loading branch information
Omer Demirok authored Oct 5, 2020
1 parent 744221f commit 4d3a309
Show file tree
Hide file tree
Showing 11 changed files with 370 additions and 33 deletions.
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,8 @@ The static resources derived from the generic resources prepended with `azure_`
- [azure_key_vaults](docs/resources/azure_key_vaults.md)
- [azure_load_balancer](docs/resources/azure_load_balancer.md)
- [azure_load_balancers](docs/resources/azure_load_balancers.md)
- [azure_lock](docs/resources/azure_lock.md)
- [azure_locks](docs/resources/azure_locks.md)
- [azure_mariadb_server](docs/resources/azure_mariadb_server.md)
- [azure_mariadb_servers](docs/resources/azure_mariadb_servers.md)
- [azure_monitor_activity_log_alert](docs/resources/azure_monitor_activity_log_alert.md)
Expand Down
93 changes: 93 additions & 0 deletions docs/resources/azure_lock.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
---
title: About the azure_lock Resource
platform: azure
---

# azure_lock

Use the `azure_lock` InSpec audit resource to test properties and configuration of a management lock.

## Azure REST API version, endpoint and http client parameters

This resource interacts with api versions supported by the resource provider.
The `api_version` can be defined as a resource parameter.
If not provided, the latest version will be used.
For more information, refer to [`azure_generic_resource`](azure_generic_resource.md).

Unless defined, `azure_cloud` global endpoint, and default values for the http client will be used.
For more information, refer to the resource pack [README](../../README.md).

## Availability

### Installation

This resource is available in the [InSpec Azure resource pack](https://github.com/inspec/inspec-azure).
For an example `inspec.yml` file and how to set up your Azure credentials, refer to resource pack [README](../../README.md#Service-Principal).

## Syntax

The management lock resources do not follow the common `resouce_group` and `name` pattern for identification.
As a result of that, the `resource_id` must be given as a parameter to the `azure_lock` resource.
The [`azure_locks`](azure_locks.md) resource can be used for gathering the management lock resource ids to be tested within the desired level, such as, subscription, resource group or individual resource.
```ruby
describe azure_lock(resource_id: '/{subscriptionId}/resourcegroups/{resourceGroupName}/providers/{resourceProviderNamespace}/{parentResourcePath}/{resourceType}/{resourceName}/providers/Microsoft.Authorization/locks/{lockName}') do
it { should exist }
end
```
## Parameters

| Name | Description |
|---------------------------------------|-----------------------------------------------------------------------------------|
| resource_id | The unique resource ID. `/{subscriptionId}/resourcegroups/{resourceGroupName}/providers/{resourceProviderNamespace}/{parentResourcePath}/{resourceType}/{resourceName}/providers/Microsoft.Authorization/locks/{lockName}` |

## Properties

| Property | Description |
|---------------------------|-------------|
| properties.level | The level of the lock. Possible values are: `NotSpecified`, `CanNotDelete`, `ReadOnly`. For more see [here](https://docs.microsoft.com/en-us/rest/api/resources/managementlocks/getatresourcelevel#locklevel). |
| properties.notes | Notes about the lock. Maximum of 512 characters. |
| properties.owners | A list of the owners of the lock with [these](https://docs.microsoft.com/en-us/rest/api/resources/managementlocks/getatresourcelevel#managementlockowner) properties. |

Please note that the properties can vary depending on the `api_version` used for the lookup.

For properties applicable to all resources, such as `type`, `name`, `id`, `properties`, refer to [`azure_generic_resource`](azure_generic_resource.md#properties).

Also, refer to [Azure documentation](https://docs.microsoft.com/en-us/rest/api/resources/managementlocks/getatresourcelevel#managementlockobject) for other properties available.
Any attribute in the response may be accessed with the key names separated by dots (`.`), eg. `properties.<attribute>`.

## Examples

### Test If a `ReadOnly` Management Lock Exist in a Specific Resource Group
```ruby
azure_locks(resource_group: 'example-group').ids.each do |id|
describe azure_lock(resource_id: id) do
its('properties.level') { should_not cmp `ReadOnly` }
end
end
```
### Test If Management Locks on a Specific Resource Contain a Certain String
```ruby
azure_locks(resouce_id: '/subscriptions/{subscriptionId}/resourceGroups/{resourceGroup}/providers/Microsoft.Compute/virtualMachines/{vmName}').ids.each do |lock_id|
describe azure_lock(resource_id: lock_id) do
it('properties.notes') { should include 'contact [email protected]' }
end
end
```
## Matchers

This InSpec audit resource has the following special matchers. For a full list of available matchers, please visit our [Universal Matchers page](https://www.inspec.io/docs/reference/matchers/).

### exists
```ruby
# If we expect a resource to always exist
describe azure_lock(resource_id: '/subscriptions/..{lockName}') do
it { should exist }
end
# If we expect a resource to never exist
describe azure_lock(resource_id: '/subscriptions/..{lockName}') do
it { should_not exist }
end
```
## Azure Permissions

Your [Service Principal](https://docs.microsoft.com/en-us/azure/azure-resource-manager/resource-group-create-service-principal-portal) must be setup with a `contributor` role on the subscription you wish to test.
114 changes: 114 additions & 0 deletions docs/resources/azure_locks.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
---
title: About the azure_locks Resource
platform: azure
---

# azure_locks

Use the `azure_locks` InSpec audit resource to test properties and configuration of all management locks for an Azure resource or any level below it.

## Azure REST API version, endpoint and http client parameters

This resource interacts with api versions supported by the resource provider.
The `api_version` can be defined as a resource parameter.
If not provided, the latest version will be used.
For more information, refer to [`azure_generic_resource`](azure_generic_resource.md).

Unless defined, `azure_cloud` global endpoint, and default values for the http client will be used.
For more information, refer to the resource pack [README](../../README.md).

## Availability

### Installation

This resource is available in the [InSpec Azure resource pack](https://github.com/inspec/inspec-azure).
For an example `inspec.yml` file and how to set up your Azure credentials, refer to resource pack [README](../../README.md#Service-Principal).

## Syntax

An `azure_locks` resource block returns all management locks, either within a Resource Group (if provided), or within an entire Subscription.
```ruby
describe azure_locks do
it { should exist }
end
```
or
```ruby
describe azure_locks(resource_group: 'my-rg') do
it { should exist }
end
```
Also, at resource level test can be done providing the following identifiers: `resource_group`, `resource_name` and `resource_type` or the `resource_id`.
```ruby
describe azure_locks(resource_group: 'rg-1', resource_name: 'my-VM', resource_type: 'Microsoft.Compute/virtualMachines') do
it { should exist }
end
```
or
```ruby
describe azure_locks(resource_id: '/subscriptions/{subscriptionId}/resourceGroups/{resourceGroup}/providers/Microsoft.Compute/virtualMachines/{vmName}') do
it { should exist }
end
```
## Parameters

| Name | Description |
|--------------------------------|----------------------------------------------------------------------------------|
| resource_group | Azure resource group that the targeted resource resides in. `MyResourceGroup` |
| resource_name | Name of the Azure resource on which the management locks are being tested. `MyVM` |
| resource_type | Type of the Azure resource on which the management locks are being tested. `Microsoft.Compute/virtualMachines` |
| resource_id | The unique resource ID of the Azure resource on which the management locks are being tested. `/subscriptions/{subscriptionId}/resourceGroups/{resourceGroup}/providers/Microsoft.Compute/virtualMachines/{vmName}` |

Either one of the parameter sets can be provided for a valid query:
- `resource_id`
- `resource_group`, `resource_name` and `resource_type`
- `resource_group`
- None for a subscription level test.

## Properties

|Property | Description | Filter Criteria<superscript>*</superscript> |
|---------------|--------------------------------------------------------------------------------------|-----------------|
| ids | A list of the unique resource ids of the management locks. | `id` |
| names | A list of names of all the management locks being interrogated. | `name` |
| properties | A list of properties for all the management locks being interrogated. | `properties` |

<superscript>*</superscript> For information on how to use filter criteria on plural resources refer to [FilterTable usage](https://github.com/inspec/inspec/blob/master/dev-docs/filtertable-usage.md).

## Examples

### Check If a Specific Management Lock is Present for a Resource
```ruby
describe azure_locks(resource_group: 'rg-1', resource_name: 'my-VM', resource_type: 'Microsoft.Compute/virtualMachines') do
its('names') { should include 'production_agents' }
end
```
### Filters the Results to Include Only Those Management Locks which Include the Given Name
```ruby
describe azure_locks.where{ name.include?('production') } do
it { should exist }
end
```
### Loop through All Virtual Machines to Test If They have Management Locks Defined on
```ruby
azure_virtual_machines.ids.each do |id|
describe azure_locks(resource_id: id) do
it { should exist }
end
end
```
## Matchers

This InSpec audit resource has the following special matchers. For a full list of available matchers, please visit our [Universal Matchers page](https://www.inspec.io/docs/reference/matchers/).

### exists

The control will pass if the filter returns at least one result. Use `should_not` if you expect zero matches.
```ruby
describe azure_locks(resource_group: 'rg-1', resource_name: 'my-VM', resource_type: 'Microsoft.Compute/virtualMachines') do
it { should exist }
end
```
## Azure Permissions

Your [Service Principal](https://docs.microsoft.com/en-us/azure/azure-resource-manager/resource-group-create-service-principal-portal) must be setup with a `contributor` role on the subscription you wish to test.
2 changes: 2 additions & 0 deletions docs/resources/azurerm_locks.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ title: About the azurerm_locks Resource
platform: azure
---

> <b>WARNING</b> This resource will be deprecated in InSpec Azure Resource Pack version **2**. Please start using fully backward compatible [`azure_locks`](azure_locks.md) InSpec audit resource.
# azurerm\_locks

Use the `azurerm_locks` InSpec audit resource to test properties of
Expand Down
2 changes: 1 addition & 1 deletion libraries/azure_backend.rb
Original file line number Diff line number Diff line change
Expand Up @@ -236,7 +236,7 @@ def get_resource(opts = {})
allow: %i(api_version),
opts: opts)
api_version = opts[:api_version] || 'latest'
if opts[:resource_uri].include?('providers')
if opts[:resource_uri].scan('providers').size == 1
# If the resource provider is unknown then this method can't find the api_version.
# The latest api_version will de acquired from the error message via #rescue_wrong_api_call method.
_resource_group, provider, r_type = Helpers.res_group_provider_type_from_uri(opts[:resource_uri])
Expand Down
29 changes: 29 additions & 0 deletions libraries/azure_lock.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
require 'azure_generic_resource'

class AzureLock < AzureGenericResource
name 'azure_lock'
desc 'Verifies settings for an Azure Lock'
example <<-EXAMPLE
describe azure_lock(resource_group: 'rg-1', name: 'my-lock-name') do
it { should exist }
end
EXAMPLE

def initialize(opts = {})
# Options should be Hash type. Otherwise Ruby will raise an error when we try to access the keys.
raise ArgumentError, 'Parameters must be provided in an Hash object.' unless opts.is_a?(Hash)
raise ArgumentError, '`resource_id` must be provided.' if opts[:resource_id].nil?
unless opts.slice(:name, :resource_group).keys.empty?
raise ArgumentError, '`name` and `resource_group` parameters are not allowed.'
end

opts[:resource_provider] = specific_resource_constraint('Microsoft.Authorization/locks', opts)

# static_resource parameter must be true for setting the resource_provider in the backend.
super(opts, true)
end

def to_s
super(AzureLock)
end
end
84 changes: 84 additions & 0 deletions libraries/azure_locks.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
require 'azure_generic_resources'

class AzureLocks < AzureGenericResources
name 'azure_locks'
desc 'Verifies settings for an Azure Lock on a Resource'
example <<-EXAMPLE
describe azure_locks(resource_group: 'my-rg', resource_name: 'my-vm', resource_type: 'Microsoft.Compute/virtualMachines') do
it { should exist }
end
EXAMPLE

attr_reader :table

def initialize(opts = {})
# Options should be Hash type. Otherwise Ruby will raise an error when we try to access the keys.
raise ArgumentError, 'Parameters must be provided in an Hash object.' unless opts.is_a?(Hash)

# Resource level parameter validation is done here due to `resource_type` is a special parameter in the backend.
if opts[:resource_id]
opts[:resource_name] = opts[:resource_id].split('/').last
opts[:resource_group], provider, r_type = Helpers.res_group_provider_type_from_uri(opts[:resource_id])
opts[:type] = [provider, r_type].join('/')
# `resource_id` is not allowed for plural resources in the backend
opts.delete(:resource_id)
elsif opts[:resource_name]
required_params = [:resource_group, :resource_name, :resource_type]
missing_params = required_params - opts.keys
raise ArgumentError, "#{missing_params} must be provided." unless missing_params.empty?
# `resource_type` is a special parameter in the backend.
# Change the key name here to something else
opts[:type] = opts[:resource_type]
opts.delete(:resource_type)
end

# This validation is done at this point due to the `resource_type` => `type` conversion has to happen before
opts[:resource_provider] = specific_resource_constraint('Microsoft.Authorization/locks', opts)
# This is for passing the validation in the backend.
opts[:allowed_parameters] = %i(resoure_id resource_group resource_name type)

opts[:resource_uri] = "/providers/#{opts[:resource_provider]}"
opts[:resource_uri].prepend("/providers/#{opts[:type]}/#{opts[:resource_name]}") unless opts[:resource_name].nil?
opts[:resource_uri].prepend("/resourceGroups/#{opts[:resource_group]}") unless opts[:resource_group].nil?
opts[:add_subscription_id] = true

# static_resource parameter must be true for setting the resource_provider in the backend.
super(opts, true)

# Check if the resource is failed.
# It is recommended to check that after every usage of inherited methods or making API calls.
return if failed_resource?

# Define the column and field names for FilterTable.
# In most cases, the `column` should be the pluralized form of the `field`.
table_schema = [
{ column: :names, field: :name },
{ column: :ids, field: :id },
{ column: :properties, field: :properties },
]

# FilterTable is populated at the very end due to being an expensive operation.
AzureGenericResources.populate_filter_table(:table, table_schema)
end

def to_s
super(AzureLocks)
end
end

# Provide the same functionality under the old resource name.
# This is for backward compatibility.
class AzurermLocks < AzureLocks
name 'azurerm_locks'
desc 'Verifies settings for an Azure Lock on a Resource'
example <<-EXAMPLE
describe azurerm_locks(resource_group: 'my-rg', resource_name: 'my-vm', resource_type: 'Microsoft.Compute/virtualMachines') do
it { should exist }
end
EXAMPLE

def initialize(opts = {})
Inspec::Log.warn Helpers.resource_deprecation_message(@__resource_name__, AzureLocks.name)
super
end
end
32 changes: 0 additions & 32 deletions libraries/azurerm_locks.rb

This file was deleted.

7 changes: 7 additions & 0 deletions test/integration/verify/controls/azurerm_locks.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,10 @@
it { should_not exist }
end
end

control 'azure_locks' do
vm_id = azure_virtual_machine(resource_group: resource_group, name: resource_name).id
describe azure_locks(resource_id: vm_id) do
it { should_not exist }
end
end
Loading

0 comments on commit 4d3a309

Please sign in to comment.