diff --git a/docs/resources/aws_s3_storage_lens.md b/docs/resources/aws_s3_storage_lens.md new file mode 100644 index 000000000..7fdc86268 --- /dev/null +++ b/docs/resources/aws_s3_storage_lens.md @@ -0,0 +1,100 @@ +--- +title: About the aws_s3_storage_lens Resource +platform: aws +--- + +# aws_s3_storage_lens + +Use the `aws_s3_storage_lens` InSpec audit resource to test the properties of the singular resource of AWS S3 StorageLens. + +## Syntax + +Ensure that S3 storage lens exists. + + describe aws_s3_storage_lens(config_id: 'CONFIG_ID', account_id: 'ACCOUNT_ID') do + it { should exist } + end + +## Parameters + +`config_id` _(required)_ + +The ID of the Amazon S3 Storage Lens configuration. + +`account_id` _(required)_ + +The account ID of the requester. + +For additional information, see the [AWS documentation on AWS S3 StorageLens.](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-s3-storagelens.html). + +## Properties + +| Property | Description | +| :----------------------------: | :----------------------------------------------------------------------------------------: | +| id | A container for the Amazon S3 Storage Lens configuration ID. | +| account_level.activity_metrics.is_enabled | A container for whether the activity metrics are enabled. | +| account_level.bucket_level.activity_metrics.is_enabled | A container for whether the activity metrics are enabled. | +| account_level.bucket_level.prefix_level.storage_metrics.is_enabled | A container for whether prefix-level storage metrics are enabled. | +| account_level.bucket_level.prefix_level.storage_metrics.selection_criteria.delimiter | A container for the delimiter of the selection criteria being used. | +| account_level.bucket_level.prefix_level.storage_metrics.selection_criteria.max_depth | The max depth of the selection criteria. | +| account_level.bucket_level.prefix_level.storage_metrics.selection_criteria.min_storage_bytes_percentage | The minimum number of storage bytes percentage whose metrics will be selected. | +| include.buckets | A container for the S3 Storage Lens bucket includes. | +| include.regions | A container for the S3 Storage Lens Region includes. | +| exclude.buckets | A container for the S3 Storage Lens bucket excludes. | +| exclude.regions | A container for the S3 Storage Lens Region excludes. | +| data_export.s3_bucket_destination.format | The format of the s3 bucket destination. | +| data_export.s3_bucket_destination.output_schema_version | The schema version of the export file. | +| data_export.s3_bucket_destination.account_id | The account ID of the owner of the S3 Storage Lens metrics export bucket. | +| data_export.s3_bucket_destination.arn | The Amazon Resource Name (ARN) of the bucket. | +| data_export.s3_bucket_destination.prefix | The prefix of the destination bucket where the metrics export will be delivered. | +| data_export.s3_bucket_destination.encryption.ssekms.key_id | A container for the ARN of the SSE-KMS encryption. | +| data_export.cloud_watch_metrics.is_enabled | A container that indicates whether CloudWatch publishing for S3 Storage Lens metrics is enabled. | +| is_enabled | A container for whether the S3 Storage Lens configuration is enabled. | +| aws_org.arn | A container for the Amazon Resource Name (ARN) of the Amazon Web Services organization. | +| storage_lens_arn | The Amazon Resource Name (ARN) of the S3 Storage Lens configuration. | + +## Examples + +### Ensure a config ID is `available`. + + describe aws_s3_storage_lens(config_id: 'CONFIG_ID', account_id: 'ACCOUNT_ID') do + its('id') { should eq 'CONFIG_ID' } + end + +### Ensure that the container is enabled. + + describe aws_s3_storage_lens(config_id: 'CONFIG_ID', account_id: 'ACCOUNT_ID') do + its('is_enabled') { should eq true } + 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/). + +The control passes if the `get` method returns at least one result. + +### exist + +Use `should` to test that the entity exists. + + describe aws_s3_storage_lens(config_id: 'CONFIG_ID', account_id: 'ACCOUNT_ID') do + it { should exist } + end + +Use `should_not` to test the entity does not exist. + + describe aws_s3_storage_lens(config_id: 'CONFIG_ID', account_id: 'ACCOUNT_ID') do + it { should_not exist } + end + +### be_available + +Use `should` to check if the entity is available. + + describe aws_s3_storage_lens(config_id: 'CONFIG_ID', account_id: 'ACCOUNT_ID') do + it { should be_available } + end + +## AWS Permissions + +Your [Principal](https://docs.aws.amazon.com/IAM/latest/UserGuide/intro-structure.html#intro-structure-principal) will need the `S3Control:Client:GetStorageLensConfigurationResult` action with `Effect` set to `Allow`. diff --git a/docs/resources/aws_s3_storage_lenses.md b/docs/resources/aws_s3_storage_lenses.md new file mode 100644 index 000000000..9b9467639 --- /dev/null +++ b/docs/resources/aws_s3_storage_lenses.md @@ -0,0 +1,79 @@ +--- +title: About the aws_s3_storage_lenses Resource +platform: aws +--- + +# aws_s3_storage_lenses + +Use the `aws_s3_storage_lenses` InSpec audit resource to test the properties of the plural resource of AWS S3 StorageLens. + +## Syntax + +Ensure that S3 storage lens exists. + + describe aws_s3_storage_lenses(config_id: 'CONFIG_ID') do + it { should exist } + end + +## Parameters + +`config_id` _(required)_ + +The ID of the Amazon S3 Storage Lens configuration. + +For additional information, see the [AWS documentation on AWS S3 StorageLens.](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-s3-storagelens.html). + +## Properties + +| Property | Description | Fields | +| :----------------------------: | :----------------------------------------------------------------------------------------: | :----------------------------------------------------------------------------------------: | +| ids | A container for the S3 Storage Lens configuration ID. | id | +| storage_lens_arns | The ARN of the S3 Storage Lens configuration. This property is read-only. | storage_lens_arn | +| home_regions | A container for the S3 Storage Lens home Region. Your metrics data is stored and retained in your designated S3 Storage Lens home Region. | home_region | +| is_enabled | A container for whether the S3 Storage Lens configuration is enabled. | is_enabled | + +## Examples + +### Ensure a config ID is `available`. + + describe aws_s3_storage_lenses(config_id: 'CONFIG_ID') do + its('ids') { should include 'CONFIG_ID' } + end + +### Ensure that the container is enabled. + + describe aws_s3_storage_lenses(config_id: 'CONFIG_ID') do + its('is_enabled') { should include true } + 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/). + +The control passes if the `get` method returns at least one result. + +### exist + +Use `should` to test that the entity exists. + + describe aws_s3_storage_lenses(config_id: 'CONFIG_ID') do + it { should exist } + end + +Use `should_not` to test the entity does not exist. + + describe aws_s3_storage_lenses(config_id: 'CONFIG_ID') do + it { should_not exist } + end + +### be_available + +Use `should` to check if the entity is available. + + describe aws_s3_storage_lenses(config_id: 'CONFIG_ID') do + it { should be_available } + end + +## AWS Permissions + +Your [Principal](https://docs.aws.amazon.com/IAM/latest/UserGuide/intro-structure.html#intro-structure-principal) will need the `S3Control:Client:GetStorageLensConfigurationResult` action with `Effect` set to `Allow`. diff --git a/libraries/aws_backend.rb b/libraries/aws_backend.rb index 853d2528b..977e6bf04 100644 --- a/libraries/aws_backend.rb +++ b/libraries/aws_backend.rb @@ -61,6 +61,7 @@ require 'aws-sdk-securityhub' require 'aws-sdk-ses' require 'aws-sdk-waf' +require 'aws-sdk-s3control' require 'aws-sdk-synthetics' # AWS Inspec Backend Classes @@ -331,6 +332,10 @@ def waf_client aws_client(Aws::WAF::Client) end + def s3control_client + aws_client(Aws::S3Control::Client) + end + def synthetics_client aws_client(Aws::Synthetics::Client) end @@ -494,7 +499,7 @@ def method_missing(method_name, *args, &block) end # This is to make RuboCop happy. - # Disabling Useless method definition detection as there is an issue with rubocop + # Disbling Useless method definition detection as there is an issue with rubocop # rubocop:disable Lint/UselessMethodDefinition def respond_to_missing?(*several_variants) super @@ -709,7 +714,7 @@ def method_missing(method_name, *args, &block) end # This is to make RuboCop happy. - # Disabling Useless method definition detection as there is an issue with rubocop + # Disbling Useless method definition detection as there is an issue with rubocop # rubocop:disable Lint/UselessMethodDefinition def respond_to_missing?(*several_variants) super @@ -749,7 +754,7 @@ def method_missing(method_name, *args, &block) end # This is to make RuboCop happy. - # Disabling Useless method definition detection as there is an issue with rubocop + # Disbling Useless method definition detection as there is an issue with rubocop # rubocop:disable Lint/UselessMethodDefinition def respond_to_missing?(*several_variants) super diff --git a/libraries/aws_s3_storage_lens.rb b/libraries/aws_s3_storage_lens.rb new file mode 100644 index 000000000..6d11c9cf1 --- /dev/null +++ b/libraries/aws_s3_storage_lens.rb @@ -0,0 +1,40 @@ +# frozen_string_literal: true + +require 'aws_backend' + +class AWSS3StorageLens < AwsResourceBase + name 'aws_s3_storage_lens' + desc 'Gets the Amazon S3 Storage Lens configuration.' + + example " + describe aws_s3_storage_lens(config_id: 'CONFIG_ID', account_id: 'ACCOUNT_ID') do + it { should exist } + end + " + + def initialize(opts = {}) + super(opts) + validate_parameters(required: %i(config_id account_id)) + raise ArgumentError, "#{@__resource_name__}: config_id must be provided" unless opts[:config_id] && !opts[:config_id].empty? + raise ArgumentError, "#{@__resource_name__}: account_id must be provided" unless opts[:account_id] && !opts[:account_id].empty? + @display_name = opts[:config_id] + catch_aws_errors do + resp = @aws.s3control_client.get_storage_lens_configuration({ config_id: opts[:config_id], account_id: opts[:account_id] }) + @res = resp.storage_lens_configuration.to_h + create_resource_methods(@res) + end + end + + def config_id + return nil unless exists? + @res[:config_id] + end + + def exists? + !@res.nil? && !@res.empty? + end + + def to_s + "S3 Storage Lens: #{@display_name}" + end +end diff --git a/libraries/aws_s3_storage_lenses.rb b/libraries/aws_s3_storage_lenses.rb new file mode 100644 index 000000000..a0d826e57 --- /dev/null +++ b/libraries/aws_s3_storage_lenses.rb @@ -0,0 +1,46 @@ +# frozen_string_literal: true + +require 'aws_backend' + +class AWSS3StorageLenses < AwsResourceBase + name 'aws_s3_storage_lenses' + desc 'Gets a list of Amazon S3 Storage Lens configurations.' + + example " + describe aws_s3_storage_lenses(account_id: 'ACCOUNT_ID') do + it { should exist } + end + " + + attr_reader :table + + FilterTable.create + .register_column(:ids, field: :id) + .register_column(:storage_lens_arns, field: :storage_lens_arn) + .register_column(:home_regions, field: :home_region) + .register_column(:is_enabled, field: :is_enabled) + .install_filter_methods_on_resource(self, :table) + + def initialize(opts = {}) + super(opts) + validate_parameters(required: %i(account_id)) + @query_params = {} + raise ArgumentError, "#{@__resource_name__}: account_id must be provided" unless opts[:account_id] && !opts[:account_id].empty? + @query_params[:account_id] = opts[:account_id] + @table = fetch_data + end + + def fetch_data + catch_aws_errors do + @table = @aws.s3control_client.list_storage_lens_configurations(@query_params).map do |table| + table.storage_lens_configuration_list.map { |table_name| { + id: table_name.id, + storage_lens_arn: table_name.storage_lens_arn, + home_region: table_name.home_region, + is_enabled: table_name.is_enabled, + } + } + end.flatten + end + end +end diff --git a/test/unit/resources/aws_s3_storage_lens_test.rb b/test/unit/resources/aws_s3_storage_lens_test.rb new file mode 100644 index 000000000..cb416fcd0 --- /dev/null +++ b/test/unit/resources/aws_s3_storage_lens_test.rb @@ -0,0 +1,52 @@ +require 'helper' +require 'aws_s3_storage_lens' +require 'aws-sdk-core' + +class AWSS3StorageLensConstructorTest < Minitest::Test + + def test_empty_params_not_ok + assert_raises(ArgumentError) { AWSS3StorageLens.new(client_args: { stub_responses: true }) } + end + + def test_empty_param_arg_not_ok + assert_raises(ArgumentError) { AWSS3StorageLens.new(config_id: '', account_id: '', client_args: { stub_responses: true }) } + end + + def test_rejects_unrecognized_params + assert_raises(ArgumentError) { AWSS3StorageLens.new(unexpected: 9) } + end +end + +class AWSS3StorageLensSuccessPathTest < Minitest::Test + + def setup + data = {} + data[:method] = :get_storage_lens_configuration + mock_data = {} + mock_data[:id] = 'test1' + mock_data[:account_level] = { + bucket_level: {} + } + mock_data[:is_enabled] = true + mock_data[:storage_lens_arn] = 'test1' + data[:data] = { storage_lens_configuration: mock_data } + data[:client] = Aws::S3Control::Client + @resp = AWSS3StorageLens.new(config_id: 'test1', account_id: 'test1', client_args: { stub_responses: true }, stub_data: [data]) + end + + def test_storage_lens_configuration_exist + assert @resp.exists? + end + + def test_id + assert_equal(@resp.id, 'test1') + end + + def test_is_enabled + assert_equal(@resp.is_enabled, true) + end + + def test_storage_lens_arn + assert_equal(@resp.storage_lens_arn, 'test1') + end +end diff --git a/test/unit/resources/aws_s3_storage_lenses_test.rb b/test/unit/resources/aws_s3_storage_lenses_test.rb new file mode 100644 index 000000000..ebbd7630e --- /dev/null +++ b/test/unit/resources/aws_s3_storage_lenses_test.rb @@ -0,0 +1,54 @@ +require 'helper' +require 'aws_s3_storage_lenses' +require 'aws-sdk-core' + +class AWSS3StorageLensesConstructorTest < Minitest::Test + + def test_empty_params_ok + AWSS3StorageLenses.new(account_id: 'test1', client_args: { stub_responses: true }) + end + + def test_rejects_other_args + assert_raises(ArgumentError) { AWSS3StorageLenses.new('rubbish') } + end + + def test_storage_lens_configuration_list_non_existing_for_empty_response + refute AWSS3StorageLenses.new(account_id: 'test1', client_args: { stub_responses: true }).exist? + end +end + +class AWSS3StorageLensesSuccessPathTest < Minitest::Test + + def setup + data = {} + data[:method] = :list_storage_lens_configurations + mock_data = {} + mock_data[:id] = 'test1' + mock_data[:storage_lens_arn] = 'test1' + mock_data[:home_region] = 'test1' + mock_data[:is_enabled] = true + data[:data] = { storage_lens_configuration_list: [mock_data] } + data[:client] = Aws::S3Control::Client + @resp = AWSS3StorageLenses.new(account_id: 'test1', client_args: { stub_responses: true }, stub_data: [data]) + end + + def test_storage_lens_configuration_list_exists + assert @resp.exists? + end + + def test_ids + assert_equal(@resp.ids, ['test1']) + end + + def test_storage_lens_arns + assert_equal(@resp.storage_lens_arns, ['test1']) + end + + def test_home_regions + assert_equal(@resp.home_regions, ['test1']) + end + + def test_is_enabled + assert_equal(@resp.is_enabled, [true]) + end +end \ No newline at end of file