Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add azure_lock(s) resources #316

Merged
merged 1 commit into from
Oct 5, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
rmoles marked this conversation as resolved.
Show resolved Hide resolved
```
## 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
rmoles marked this conversation as resolved.
Show resolved Hide resolved
# 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