diff --git a/docs/resources/aws_cloudfront_key_group.md b/docs/resources/aws_cloudfront_key_group.md new file mode 100644 index 000000000..0c0335575 --- /dev/null +++ b/docs/resources/aws_cloudfront_key_group.md @@ -0,0 +1,80 @@ +--- +title: About the aws_cloudfront_key_group Resource +platform: aws +--- + +# aws_cloudfront_key_group + +Use the `aws_cloudfront_key_group` InSpec audit resource to test properties of a single AWS CloudFront key group. + +## Syntax + +Ensure that the key group exists. + + describe aws_cloudfront_key_group(id: "ID") do + it { should exist } + end + +## Parameters + +`id` _(required)_ + +The identifier for the key group. + +For additional information, see the [AWS documentation on AWS CloudFront key group.](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-cloudfront-keygroup.html). + +## Properties + +| Property | Description | +| --- | --- | +| id | The identifier for the key group. | +| last_modified_time | The date and time when the key group was last modified. | +| key_group_config.name | A name to identify the key group. | name | +| key_group_config.items | A list of the identifiers of the public keys in the key group. | +| key_group_config.comment | A comment to describe the key group. The comment cannot be longer than 128 characters. | + +## Examples + +### Ensure an ID is available. + + describe aws_cloudfront_key_group(id: "ID") do + its('id') { should eq 'ID' } + end + +### Ensure that the key group name is available. + + describe aws_cloudfront_key_group(id: "ID") do + its('key_group_config.name') { should eq 'KEY_GROUP_NAME' } + 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 controls will pass if the `get` method returns at least one result. + +### exist + +Use `should` to test that the entity exists. + + describe aws_cloudfront_key_group(id: "ID") do + it { should exist } + end + +Use `should_not` to test the entity does not exist. + + describe aws_cloudfront_key_group(id: "ID") do + it { should_not exist } + end + +### be_available + +Use `should` to check if the entity is available. + + describe aws_cloudfront_key_group(id: "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 `CloudFront:Client:GetKeyGroupResult` action with `Effect` set to `Allow`. diff --git a/docs/resources/aws_cloudfront_key_groups.md b/docs/resources/aws_cloudfront_key_groups.md new file mode 100644 index 000000000..b9bceb623 --- /dev/null +++ b/docs/resources/aws_cloudfront_key_groups.md @@ -0,0 +1,68 @@ +--- +title: About the aws_cloudfront_key_groups Resource +platform: aws +--- + +# aws_cloudfront_key_groups + +Use the `aws_cloudfront_key_groups` InSpec audit resource to test properties of multiple AWS CloudFront key groups. + +## Syntax + +Ensure that the key group exists. + + describe aws_cloudfront_key_groups do + it { should exist } + end + +## Parameters + +For additional information, see the [AWS documentation on AWS CloudFront key group.](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-cloudfront-keygroup.html). + +## Properties + +| Property | Description | Field | +| --- | --- | --- | +| ids | The identifier for the key group. | id | +| last_modified_times | The date and time when the key group was last modified. | last_modified_time | +| names | A name to identify the key group. | name | +| items | A list of the identifiers of the public keys in the key group. | items | +| comments | A comment to describe the key group. The comment cannot be longer than 128 characters. | comment | + +## Examples + +### Ensure an ID is available. + + describe aws_cloudfront_key_groups do + its('ids') { should include 'ID' } + end + +### Ensure that the key group name is available. + + describe aws_cloudfront_key_groups do + its('names') { should include 'KEY_GROUP_NAME' } + 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 controls will pass if the `list` method returns at least one result. + +### exist + +Use `should` to test that the entity exists. + + describe aws_cloudfront_key_groups do + it { should exist } + end + +Use `should_not` to test that an entity does not exist. + + describe aws_cloudfront_key_groups do + it { should_not exist } + end + +## AWS Permissions + +Your [Principal](https://docs.aws.amazon.com/IAM/latest/UserGuide/intro-structure.html#intro-structure-principal) will need the `CloudFront:Client:ListKeyGroupsResult` action with `Effect` set to `Allow`. diff --git a/libraries/aws_cloudfront_key_group.rb b/libraries/aws_cloudfront_key_group.rb new file mode 100644 index 000000000..595b87120 --- /dev/null +++ b/libraries/aws_cloudfront_key_group.rb @@ -0,0 +1,40 @@ +# frozen_string_literal: true + +require 'aws_backend' + +class AwsCloudFrontKeyGroup < AwsResourceBase + name 'aws_cloudfront_key_group' + desc 'Describes one Key Group.' + + example " + describe aws_cloudfront_key_group(id: 'KEY_GROUP_ID') do + it { should exist } + end + " + + def initialize(opts = {}) + opts = { id: opts } if opts.is_a?(String) + super(opts) + validate_parameters(required: [:id]) + raise ArgumentError, "#{@__resource_name__}: id must be provided" unless opts[:id] && !opts[:id].empty? + @display_name = opts[:id] + catch_aws_errors do + resp = @aws.cloudfront_client.get_key_group({ id: opts[:id] }) + @resp = resp.key_group.to_h + create_resource_methods(@resp) + end + end + + def id + return nil unless exists? + @resp[:id] + end + + def exists? + !@resp.nil? && !@resp.empty? + end + + def to_s + "Key Group ID: #{@display_name}" + end +end diff --git a/libraries/aws_cloudfront_key_groups.rb b/libraries/aws_cloudfront_key_groups.rb new file mode 100644 index 000000000..fd23c9b41 --- /dev/null +++ b/libraries/aws_cloudfront_key_groups.rb @@ -0,0 +1,45 @@ +# frozen_string_literal: true + +require 'aws_backend' + +class AwsCloudFrontKeyGroups < AwsResourceBase + name 'aws_cloudfront_key_groups' + desc 'Describes one or more of Key Group.' + + example " + describe aws_cloudfront_key_groups do + it { should exist } + end + " + + attr_reader :table + + FilterTable.create + .register_column(:ids, field: :id) + .register_column(:last_modified_time, field: :last_modified_time) + .register_column(:key_group_config_names, field: :key_group_config_name) + .register_column(:key_group_config_items, field: :key_group_config_items, style: :simple) + .register_column(:key_group_config_comments, field: :key_group_config_comment, style: :simple) + .install_filter_methods_on_resource(self, :table) + + def initialize(opts = {}) + super(opts) + validate_parameters + @table = fetch_data + end + + def fetch_data + catch_aws_errors do + @resp = @aws.cloudfront_client.list_key_groups.map do |table| + table.key_group_list.items.map { |table_name| { + id: table_name.key_group.id, + last_modified_time: table_name.key_group.last_modified_time, + key_group_config_name: table_name.key_group.key_group_config.name, + key_group_config_items: table_name.key_group.key_group_config.items, + key_group_config_comment: table_name.key_group.key_group_config.comment, + } + } + end.flatten + end + end +end diff --git a/test/integration/build/aws.tf b/test/integration/build/aws.tf index 5144fd9bd..7816507e1 100644 --- a/test/integration/build/aws.tf +++ b/test/integration/build/aws.tf @@ -4973,3 +4973,21 @@ resource "aws_cloudfront_public_key" "test_cf_pk" { name = "test_key" } + + +## Cloudfront key Group +locals { + test_key = "${path.module}/pubkey.pem" +} + +resource "aws_cloudfront_public_key" "test_cf_pk" { + comment = "test public key" + encoded_key = file(local.test_key) + name = "test_key" +} + +resource "aws_cloudfront_key_group" "example" { + comment = "example key group" + items = [aws_cloudfront_public_key.test_cf_pk.id] + name = "example-key-group" +} diff --git a/test/integration/build/outputs.tf b/test/integration/build/outputs.tf index a77a91915..11ab40435 100644 --- a/test/integration/build/outputs.tf +++ b/test/integration/build/outputs.tf @@ -934,6 +934,18 @@ output "aws_api_gateway_client_certificate_pem_encoded_certificate" { value = aws_api_gateway_client_certificate.aws_api_gateway_client_certificate_test1.pem_encoded_certificate } +output "aws_cloudfront_group_id" { + value = aws_cloudfront_key_group.example.id +} + +output "aws_cloudfront_group_name" { + value = aws_cloudfront_key_group.example.name +} + +output "aws_cloudfront_group_comment" { + value = aws_cloudfront_key_group.example.comment +} + output "broker_id" { value = aws_mq_broker.test-broker.id } diff --git a/test/integration/verify/controls/aws_cloudfront_key_group.rb b/test/integration/verify/controls/aws_cloudfront_key_group.rb new file mode 100644 index 000000000..04be8edde --- /dev/null +++ b/test/integration/verify/controls/aws_cloudfront_key_group.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true +aws_cloudfront_group_id = attribute('aws_cloudfront_group_id', value: '', description: '') +aws_cloudfront_group_name = attribute('aws_cloudfront_group_name', value: '(none)', description: '') +aws_cloudfront_group_comment = attribute('aws_cloudfront_group_comment', value: '(none)', description: '') + +control 'aws_cloudfront_public_key-1.0' do + impact 1.0 + title 'Describes the AWS cloudfront keys group.' + describe aws_cloudfront_key_group(id: aws_cloudfront_group_id) do + it { should exist } + its('id') { should eq aws_cloudfront_group_id } + its('key_group_config.name') { should eq aws_cloudfront_group_name } + its('key_group_config.comment') { should eq aws_cloudfront_group_comment } + end +end diff --git a/test/integration/verify/controls/aws_cloudfront_key_groups.rb b/test/integration/verify/controls/aws_cloudfront_key_groups.rb new file mode 100644 index 000000000..6bb2171ad --- /dev/null +++ b/test/integration/verify/controls/aws_cloudfront_key_groups.rb @@ -0,0 +1,13 @@ +# frozen_string_literal: true +aws_cloudfront_group_id = attribute('aws_cloudfront_group_id', value: '', description: '') +aws_cloudfront_group_name = attribute('aws_cloudfront_group_name', value: '(none)', description: '') + +control 'aws_cloudfront_key_groups-1.0' do + impact 1.0 + title 'Describes the AWS cloudfront keys group.' + describe aws_cloudfront_key_groups do + it { should exist } + its('key_group_config_names') { should include aws_cloudfront_group_name } + its('ids') { should include aws_cloudfront_group_id } + end +end diff --git a/test/unit/resources/aws_cloudfront_key_group_test.rb b/test/unit/resources/aws_cloudfront_key_group_test.rb new file mode 100644 index 000000000..b8e0a0594 --- /dev/null +++ b/test/unit/resources/aws_cloudfront_key_group_test.rb @@ -0,0 +1,61 @@ +require 'helper' +require 'aws_cloudfront_key_group' +require 'aws-sdk-core' + +class AwsCloudFrontKeyGroupConstructorTest < Minitest::Test + + def test_empty_params_not_ok + assert_raises(ArgumentError) { AwsCloudFrontKeyGroup.new(client_args: { stub_responses: true }) } + end + + def test_empty_param_arg_not_ok + assert_raises(ArgumentError) { AwsCloudFrontKeyGroup.new(id: '', client_args: { stub_responses: true }) } + end + + def test_rejects_unrecognized_params + assert_raises(ArgumentError) { AwsCloudFrontKeyGroup.new(unexpected: 9) } + end +end + +class AwsCloudFrontKeyGroupSuccessPathTest < Minitest::Test + + def setup + data = {} + data[:method] = :get_key_group + mock_data = {} + mock_data[:id] = 'test1' + mock_data[:last_modified_time] = Time.parse('2022-09-08 01:56:24.000000000 +0000') + mock_data[:key_group_config] = { + name: 'test1', + items: [], + comment: 'test1' + } + data[:data] = { key_group: mock_data } + data[:client] = Aws::CloudFront::Client + @resp = AwsCloudFrontKeyGroup.new(id: 'test1', client_args: { stub_responses: true }, stub_data: [data]) + end + + def test_key_group_exists + assert @resp.exists? + end + + def test_id + assert_equal(@resp.id, 'test1') + end + + def test_last_modified_time + assert_equal(@resp.last_modified_time, Time.parse('2022-09-08 01:56:24.000000000 +0000')) + end + + def test_name + assert_equal(@resp.key_group_config.name, 'test1') + end + + def test_items + assert_equal(@resp.key_group_config.items, []) + end + + def test_comment + assert_equal(@resp.key_group_config.comment, 'test1') + end +end \ No newline at end of file diff --git a/test/unit/resources/aws_cloudfront_key_groups_test.rb b/test/unit/resources/aws_cloudfront_key_groups_test.rb new file mode 100644 index 000000000..cd8f43c67 --- /dev/null +++ b/test/unit/resources/aws_cloudfront_key_groups_test.rb @@ -0,0 +1,67 @@ +require 'helper' +require 'aws_cloudfront_key_groups' +require 'aws-sdk-core' + +class AwsCloudFrontKeyGroupsConstructorTest < Minitest::Test + + def test_empty_params_ok + AwsCloudFrontKeyGroups.new(client_args: { stub_responses: true }) + end + + def test_rejects_other_args + assert_raises(ArgumentError) { AwsCloudFrontKeyGroups.new('rubbish') } + end + + def test_key_groups_non_existing_for_empty_response + refute AwsCloudFrontKeyGroups.new(client_args: { stub_responses: true }).exist? + end +end + +class AwsCloudFrontKeyGroupsHappyPathTest < Minitest::Test + + def setup + data = {} + data[:method] = :list_key_groups + mock_data = {} + mock_data[:max_items] = 1 + mock_data[:quantity] = 1 + mock_data[:items] = [ + key_group: { + id: 'test1', + last_modified_time: Time.parse('2022-09-08 01:56:24.000000000 +0000'), + key_group_config: { + name: 'test1', + items: [], + comment: 'test1' + } + } + ] + data[:data] = { :key_group_list => mock_data } + data[:client] = Aws::CloudFront::Client + @resp = AwsCloudFrontKeyGroups.new(client_args: { stub_responses: true }, stub_data: [data]) + end + + def test_key_group_exists + assert @resp.exists? + end + + def test_ids + assert_equal(@resp.ids, ['test1']) + end + + def test_last_modified_times + assert_equal(@resp.last_modified_time, [Time.parse('2022-09-08 01:56:24.000000000 +0000')]) + end + + def test_names + assert_equal(@resp.key_group_config_names, ['test1']) + end + + def test_items + assert_equal(@resp.key_group_config_items, []) + end + + def test_comments + assert_equal(@resp.key_group_config_comments, ['test1']) + end +end \ No newline at end of file