Skip to content

Commit

Permalink
rewrite storage account to new generic resource way (WIP) (#324)
Browse files Browse the repository at this point in the history
* rewrite storage account to new generic resource way

Signed-off-by: srb3 <sbrown@chef.io>

* remove duplicate to_s

* adding first pass at storage account docs

Signed-off-by: srb3 <sbrown@chef.io>

* Updat azure storage account

Signed-off-by: Omer Demirok <odemirok@chef.io>

* Update azure storage account resource

Signed-off-by: Omer Demirok <odemirok@chef.io>

* Fix a typo

Signed-off-by: Omer Demirok <odemirok@chef.io>

Co-authored-by: Ross <rmoles@users.noreply.github.com>
Co-authored-by: Omer Demirok <odemirok@chef.io>
  • Loading branch information
3 people authored Nov 10, 2020
1 parent 3df94da commit 3dcabe4
Show file tree
Hide file tree
Showing 12 changed files with 470 additions and 116 deletions.
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_storage_account(resource_id: '/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.Storage/storageAccounts/{accountName}') do
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

0 comments on commit 3dcabe4

Please sign in to comment.