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

rewrite storage account to new generic resource way (WIP) #324

Merged
merged 10 commits into from
Nov 10, 2020
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,8 @@ The static resources derived from the generic resources prepended with `azure_`
- [azure_sql_databases](docs/resources/azure_sql_databases.md)
- [azure_sql_server](docs/resources/azure_sql_server.md)
- [azure_sql_servers](docs/resources/azure_sql_servers.md)
- [azure_storage_account](docs/resources/azure_storage_account.md)
- [azure_storage_accounts](docs/resources/azure_storage_accounts.md)
- [azure_storage_account_blob_container](docs/resources/azure_storage_account_blob_container.md)
- [azure_storage_account_blob_containers](docs/resources/azure_storage_account_blob_containers.md)
- [azure_subnet](docs/resources/azure_subnet.md)
Expand Down
132 changes: 132 additions & 0 deletions docs/resources/azure_storage_account.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
---
title: About the azure_storage_account Resource
platform: azure
---

# azure_storage_account

Use the `azure_storage_account` InSpec audit resource to test properties related to an Azure Storage Account.

## 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_storage_account` resource block identifies an Azure storage account by `name` and `resource_group` or the `resource_id`.
```ruby
describe azure_storage_account(resource_group: 'rg', name: 'sa') do
it { should exist }
end
```
```ruby
describe azure_aks_cluster(resource_id: '/subscriptions/{subscriptionId}/resourceGroups/{resourceGroup}/providers/Microsoft.ContainerService/managedClusters/{ClusterName}') do
rmoles marked this conversation as resolved.
Show resolved Hide resolved
it { should exist }
end
```
## Parameters

| Name | Description |
|--------------------------------|--------------------------------------------------------------------------------------|
| resource_group | Azure resource group that the targeted resource resides in. `MyResourceGroup` |
| name | The name of the storage account within the specified resource group. `accountName` |
| resource_id | The unique resource ID. `/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.Storage/storageAccounts/{accountName}` |
| activity_log_alert_api_version | The activity log alerts endpoint api version used in `have_recently_generated_access_key` matcher. The latest version will be used unless provided. |
| storage_service_endpoint_api_version | The storage service endpoint api version. `2019-12-12` wil be used unless provided. |

Either one of the parameter sets can be provided for a valid query:
- `resource_id`
- `resource_group` and `name`

## Properties

| Property | Description |
|----------------------------------------------|-------------|
| queues<superscript>*</superscript> | Lists all of the queues in a given storage account. See [here](https://docs.microsoft.com/en-us/rest/api/storageservices/list-queues1) for more.
| queue_properties<superscript>*</superscript> | gets the properties of a storage account’s Queue service, including properties for Storage Analytics and CORS (Cross-Origin Resource Sharing) rules. See [here](https://docs.microsoft.com/en-us/rest/api/storageservices/get-queue-service-properties) for more.

<superscript>*</superscript>: Note that the Azure endpoints return data in XML format; however, they will be converted to Azure Resource Probe to make the properties accessible via dot notation.
The property names will be in snake case, `propety_name`. Therefore, `<EnumerationResults ServiceEndpoint="https://myaccount.queue.core.windows.net/">` can be tested via `its('enumeration_results.service_endpoint)`.

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/storagerp/storageaccounts/getproperties#storageaccount) for other properties available.
Any attribute in the response may be accessed with the key names separated by dots (`.`).

## Examples

### Test the Primary Endpoints
```ruby
describe azure_storage_account(resource_group: 'rg', name: 'mysa') do
its('properties.primaryEndpoints.blob') { should cmp 'https://mysa.blob.core.windows.net/' }
its('properties.primaryEndpoints.queue') { should cmp 'https://mysa.queue.core.windows.net/' }
its('properties.primaryEndpoints.table') { should cmp 'https://mysa.table.core.windows.net/' }
its('properties.primaryEndpoints.file') { should cmp 'https://mysa.file.core.windows.net/' }
end
```
### Verify that Only HTTPs is Supported
```ruby
describe azure_storage_account(resource_group: 'rg', name: 'mysa') do
its('properties.supportsHttpsTrafficOnly') { should be true }
end
```
### Test Queues Service Endpoint
```ruby
describe azure_storage_account(resource_group: 'rg', name: 'mysa') do
its('queues.enumeration_results.service_endpoint') { should cmp 'https://mysa.queue.core.windows.net/' }
end
```
### Test Queue Properties Logging Version
```ruby
describe azure_storage_account(resource_group: 'rg', name: 'mysa') do
its('queue_properties.logging.version') { should cmp '1.0' }
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://docs.chef.io/inspec/matchers/).

### have_encryption_enabled

Test if encryption is enabled.
```ruby
describe azure_storage_account(resource_group: 'rg', name: 'mysa') do
it { should have_encryption_enabled }
end
```
### have_recently_generated_access_key

Test if an access key has been generated within the last **90** days.
```ruby
describe azure_storage_account(resource_group: 'rg', name: 'mysa') do
it { should have_recently_generated_access_key }
end
```
### exists
```ruby
# If we expect the resource to always exist
describe azure_storage_account(resource_group: 'rg', name: 'mysa') do
it { should exist }
end

# If we expect the resource to never exist
describe azure_storage_account(resource_group: 'rg', name: 'mysa') 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.
86 changes: 86 additions & 0 deletions docs/resources/azure_storage_accounts.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
---
title: About the azure_storage_accounts Resource
platform: azure
---

# azure_storage_accounts

Use the `azure_storage_accounts` InSpec audit resource to test properties and configuration of multiple Azure Storage Accounts.

## 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_storage_accounts` resource block returns all Azure storape accounts, either within a Resource Group (if provided), or within an entire Subscription.
```ruby
describe azure_storage_accounts do
#...
end
```
or
```ruby
describe azure_storage_accounts(resource_group: 'my-rg') do
#...
end
```
## Parameters

- `resource_group` (Optional)

## Properties

|Property | Description | Filter Criteria<superscript>*</superscript> |
|---------------|--------------------------------------------------------------------------------------|-----------------|
| ids | A list of the unique resource ids. | `id` |
| locations | A list of locations for all the resources being interrogated. | `location` |
| names | A list of names of all the resources being interrogated. | `name` |
| type | A list of types of all the resources being interrogated. | `type` |
| tags | A list of `tag:value` pairs defined on the resources being interrogated. | `tags` |

<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 Storage Account Exists
```ruby
describe azurerm_storage_accounts(resource_group: 'rg') do
its('names') { should include('mysa') }
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
# If we expect at least one account to exist in a resource group
describe azure_storage_accounts(resource_group: 'rg') do
it { should exist }
end

# If we expect no storage accounts to exist in a resource group
describe azure_storage_accounts(resource_group: 'rg') 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.
131 changes: 131 additions & 0 deletions libraries/azure_storage_account.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
require 'azure_generic_resource'
require 'active_support/core_ext/hash'

class AzureStorageAccount < AzureGenericResource
name 'azure_storage_account'
desc 'Verifies settings for a Azure Storage Account'
example <<-EXAMPLE
describe azure_storage_account(resource_group: 'r-group', name: 'default') 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)

opts[:resource_provider] = specific_resource_constraint('Microsoft.Storage/storageAccounts', opts)
opts[:allowed_parameters] = %i(activity_log_alert_api_version storage_service_endpoint_api_version)
# fall-back `api_version` is fixed for now.
# TODO: Implement getting the latest Azure Storage services api version
opts[:storage_service_endpoint_api_version] ||= '2019-12-12'
opts[:activity_log_alert_api_version] ||= 'latest'

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

def to_s
super(AzureKeyVault)
end

# Resource specific methods can be created.
# `return unless exists?` is necessary to prevent any unforeseen Ruby error.
# Following methods are created to provide the same functionality with the current resource pack >>>>
# @see https://github.com/inspec/inspec-azure

def has_recently_generated_access_key?
return unless exists?
now = Time.now
ninety_days_ago = ((60*60)*(24*90))
upper_bound = to_utc(now)
lower_bound = to_utc(now - ninety_days_ago)

filter = "resourceId eq '#{id}' and "\
"eventTimestamp ge '#{lower_bound}' and "\
"eventTimestamp le '#{upper_bound}' and "\
"operations eq 'Microsoft.Storage/storageAccounts/regeneratekey/action'"
activity_log_alert_filter(filter) unless respond_to?(:activity_log_alert_filtered)
activity_log_alert_filtered.any?
end

def has_encryption_enabled?
return unless exists?
properties.encryption.services.blob.enabled || false
end

def queues
return unless exists?
url = "https://#{name}.queue#{@azure.storage_endpoint_suffix}"
param = { comp: 'list' }
# Calls to Azure Storage resources requires a special header `x-ms-version`
# https://docs.microsoft.com/en-us/rest/api/storageservices/versioning-for-the-azure-storage-services
headers = { 'x-ms-version' => @opts[:storage_service_endpoint_api_version] }
body = @azure.rest_get_call(url, param, headers)
return unless body
body_hash = Hash.from_xml(body)
hash_with_snakecase_keys = RecursiveMethodHelper.method_recursive(body_hash, :snakecase)
if hash_with_snakecase_keys
create_resource_methods({ queues: hash_with_snakecase_keys })
public_send(:queues) if respond_to?(:queues)
end
end

def queue_properties
return unless exists?
url = "https://#{name}.queue#{@azure.storage_endpoint_suffix}"
param = { restype: 'service', comp: 'properties' }
# @see #queues for the header `x-ms-version`
headers = { 'x-ms-version' => @opts[:storage_service_endpoint_api_version] }
body = @azure.rest_get_call(url, param, headers)
return unless body
body_hash = Hash.from_xml(body)
hash_with_snakecase_keys = RecursiveMethodHelper.method_recursive(body_hash, :snakecase)
properties = hash_with_snakecase_keys['storage_service_properties']
if properties
create_resource_methods({ queue_properties: properties })
public_send(:queue_properties) if respond_to?(:queue_properties)
end
end

private

# @see AzureKeyVault#diagnostic_settings for how to use #additional_resource_properties method.
#
def activity_log_alert_filter(filter)
return unless exists?
# `additional_resource_properties` method will create a singleton method with the `property_name`
# and make api response available through this property.
additional_resource_properties(
{
property_name: 'activity_log_alert_filtered',
property_endpoint: '/providers/microsoft.insights/eventtypes/management/values',
add_subscription_id: true,
api_version: @opts[:activity_log_alert_api_version],
filter_free_text: filter,
},
)
end

def to_utc(datetime)
# API requires times in UTC ISO8601 format.
datetime.to_time.utc.iso8601
end
end

# Provide the same functionality under the old resource name.
# This is for backward compatibility.
class AzurermStorageAccount < AzureStorageAccount
name 'azurerm_storage_account'
desc 'Verifies settings for a Azure Storage Account'
example <<-EXAMPLE
describe azurerm_storage_account(resource_group: resource_name, name: 'default') do
it { should exist }
end
EXAMPLE

def initialize(opts = {})
Inspec::Log.warn Helpers.resource_deprecation_message(@__resource_name__, AzureStorageAccount.name)
super
end
end
Loading