New Resource aws_config_recorder (#2635)

* Initial commit of new resource
* Removes deprecated matcher in example
* Adds a new terraform file for config resources
* Fixes and clarifies documentation
* Wraps calls to api in catch_aws_errors method
* Changes the names of two matchers

Signed-off-by: Matthew Dromazos <dromazmj@dukes.jmu.edu>
This commit is contained in:
Matthew Dromazos 2018-02-27 13:15:04 -05:00 committed by Jared Quick
parent 4e6b3bb1ba
commit 4394c5efc8
6 changed files with 411 additions and 0 deletions

View file

@ -0,0 +1,71 @@
---
title: About the aws_config_recorder Resource
---
# aws\_config\_recorder
Use the `aws_config_recorder` InSpec audit resource to test properties of your AWS Config Service.
The AWS Config service can monitor and record changes to your AWS resource configurations. The Aws Config Recorder is used to detect changes in resource configurations and capture these changes as configuration items.
<br>
## Syntax
An `aws_config_recorder` resource block declares the tests for a single AWS configuration recorder.
describe aws_config_recorder('my_recorder') do
it { should exist }
end
describe aws_config_recorder(recorder_name: 'my-recorder') do
it { should exist }
end
<br>
## Examples
The following examples show how to use this InSpec audit resource.
### Test if the recorder is active and recording.
describe aws_config_recorder(recorder_name: 'my-recorder') do
it { should be_recording }
end
## Properties
### role\_arn
Provides the IAM role arn associated with the configuration recorder. The role is used to grant permissions to S3 Buckets, SNS topics and to get configuration details for supported AWS resources.
describe aws_config_recorder(username: 'bob')
its('role_arn') { should eq 'arn:aws:iam::721741954427:role/My_Recorder' }
end
### resource\_types
Provides a list of AWS resource types for which the AWS Config records configuration will change. Note that if be_recording_all_resource_types is true than this property is meaningless and will return and empty array.
describe aws_config_recorder(username: 'bob')
its('resource_types') { should include 'AWS::EC2::CustomerGateway' }
its('resource_types') { should include 'AWS::EC2::EIP' }
end
<br>
## Matchers
### be\_recording\_all\_resource\_types
Indicates if the ConfigurationRecorder will record changes for all resources, regardless of type. If this is true, resource_types is ignored.
it { should be_all_supported }
### be\_recording\_all\_global\_types
Indicates whether the ConfigurationRecorder will record changes for global resource types (such as IAM Users).
it { should be_recording_all_global_types }

View file

@ -16,6 +16,7 @@ require 'resources/aws/aws_cloudtrail_trail'
require 'resources/aws/aws_cloudtrail_trails'
require 'resources/aws/aws_cloudwatch_alarm'
require 'resources/aws/aws_cloudwatch_log_metric_filter'
require 'resources/aws/aws_config_recorder'
require 'resources/aws/aws_ec2_instance'
require 'resources/aws/aws_iam_access_key'
require 'resources/aws/aws_iam_access_keys'

View file

@ -0,0 +1,98 @@
class AwsConfigurationRecorder < Inspec.resource(1)
name 'aws_config_recorder'
desc 'Verifies settings for AWS Configuration Recorder'
example "
describe aws_config_recorder('My_Recorder') do
it { should exist }
it { should be_recording }
it { should be_all_supported }
it { should have_include_global_resource_types }
end
"
supports platform: 'aws'
include AwsSingularResourceMixin
attr_reader :role_arn, :resource_types, :recorder_name, :resp
def to_s
"Configuration_Recorder: #{@recorder_name}"
end
def recording_all_resource_types?
@recording_all_resource_types
end
def recording_all_global_types?
@recording_all_global_types
end
def status
return unless @exists
backend = BackendFactory.create(inspec_runner)
catch_aws_errors do
@resp = backend.describe_configuration_recorder_status(@query)
@status = @resp.configuration_recorders_status.first.to_h
end
end
def recording?
return unless @exists
status[:recording]
end
private
def validate_params(raw_params)
validated_params = check_resource_param_names(
raw_params: raw_params,
allowed_params: [:recorder_name],
allowed_scalar_name: :recorder_name,
allowed_scalar_type: String,
)
# Must give it a recorder_name
if validated_params[:recorder_name].nil?
raise ArgumentError, 'You must provide recorder_name to aws_config_recorder'
end
validated_params
end
def fetch_from_api
backend = BackendFactory.create(inspec_runner)
@query = { configuration_recorder_names: [@recorder_name] }
catch_aws_errors do
begin
@resp = backend.describe_configuration_recorders(@query)
rescue Aws::ConfigService::Errors::NoSuchConfigurationRecorderException
@exists = false
return
end
@exists = !@resp.empty?
return unless @exists
@recorder = @resp.configuration_recorders.first.to_h
@recorder_name = @recorder[:name]
@role_arn = @recorder[:role_arn]
@recording_all_resource_types = @recorder[:recording_group][:all_supported]
@recording_all_global_types = @recorder[:recording_group][:include_global_resource_types]
@resource_types = @recorder[:recording_group][:resource_types]
end
end
class Backend
class AwsClientApi < AwsBackendBase
BackendFactory.set_default_backend(self)
self.aws_client_class = Aws::ConfigService::Client
def describe_configuration_recorders(query)
aws_service_client.describe_configuration_recorders(query)
end
def describe_configuration_recorder_status(query)
aws_service_client.describe_configuration_recorder_status(query)
end
end
end
end

View file

@ -0,0 +1,36 @@
#======================================================#
# Configuration Recorder
#======================================================#
resource "aws_config_configuration_recorder" "config_recorder" {
name = "config_recorder"
role_arn = "${aws_iam_role.role_for_config_recorder.arn}"
}
resource "aws_iam_role" "role_for_config_recorder" {
name = "role_for_config_recorder"
assume_role_policy = <<POLICY
{
"Version": "2012-10-17",
"Statement": [
{
"Action": "sts:AssumeRole",
"Principal": {
"Service": "config.amazonaws.com"
},
"Effect": "Allow",
"Sid": ""
}
]
}
POLICY
}
output "role_for_config_recorder_arn" {
value = "${aws_iam_role.role_for_config_recorder.arn}"
}
output "config_recorder_name" {
value = "${aws_config_configuration_recorder.config_recorder.name}"
}

View file

@ -0,0 +1,52 @@
fixtures = {}
[
'role_for_config_recorder_arn',
'config_recorder_name',
].each do |fixture_name|
fixtures[fixture_name] = attribute(
fixture_name,
default: "default.#{fixture_name}",
description: 'See ../build/config.tf',
)
end
#======================================================#
# aws_config_recorder - Singular
#======================================================#
#------------------- Recall / Miss -------------------#
control "aws_config_recorder recall" do
# Test scalar param
describe aws_config_recorder(fixtures['config_recorder_name']) do
it { should exist }
end
# Test hash parameter
describe aws_config_recorder(recorder_name: fixtures['config_recorder_name']) do
it { should exist }
end
# Test recorder that doesnt exist
describe aws_config_recorder(recorder_name: 'NonExistentRecorder') do
it { should_not exist }
end
end
#------------------- Properties -------------------#
control "aws_config_recorder properties" do
describe aws_config_recorder(fixtures['config_recorder_name']) do
its('recorder_name') { should eq fixtures['config_recorder_name'] }
its('role_arn') { should eq fixtures['role_for_config_recorder_arn'] }
its('resource_types') { should eq [] }
end
end
#------------------- Matchers -------------------#
control "aws_config_recorder matchers" do
describe aws_config_recorder(fixtures['config_recorder_name']) do
it { should_not be_recording }
it { should be_recording_all_resource_types }
it { should_not be_recording_all_global_types }
end
end

View file

@ -0,0 +1,153 @@
# encoding: utf-8
require 'helper'
# MCRSB = MockConfigRecorderSingleBackend
# Abbreviation not used outside this file
#=============================================================================#
# Constructor Tests
#=============================================================================#
class AwsConfigurationRecorderConstructorTest < Minitest::Test
def setup
AwsConfigurationRecorder::BackendFactory.select(AwsMCRSB::Basic)
end
def test_constructor_expected_well_formed_args_scalar
AwsConfigurationRecorder.new('default')
end
def test_constructor_expected_well_formed_args_hash
AwsConfigurationRecorder.new(recorder_name: 'default')
end
def test_constructor_reject_no_params
assert_raises(ArgumentError) { AwsConfigurationRecorder.new }
end
def test_constructor_reject_unknown_resource_params
assert_raises(ArgumentError) { AwsConfigurationRecorder.new(bla: 'blabla') }
end
end
#=============================================================================#
# Recall
#=============================================================================#
class AwsConfigurationRecorderRecallTest < Minitest::Test
def setup
AwsConfigurationRecorder::BackendFactory.select(AwsMCRSB::Basic)
end
def test_search_hit_via_scalar
assert AwsConfigurationRecorder.new('default').exists?
end
def test_search_hit_via_hash
assert AwsConfigurationRecorder.new(recorder_name: 'default').exists?
end
def test_search_miss_is_not_an_exception
refute AwsConfigurationRecorder.new(recorder_name: 'NonExistentRecorder').exists?
end
end
#=============================================================================#
# properties
#=============================================================================#
class AwsConfigurationRecorderPropertiesTest < Minitest::Test
def setup
AwsConfigurationRecorder::BackendFactory.select(AwsMCRSB::Basic)
end
def test_property_recorder_name
assert_equal('default', AwsConfigurationRecorder.new(recorder_name: 'default').recorder_name)
end
def test_property_role_arn
assert_equal('arn:aws:iam::721741954427:role/default', AwsConfigurationRecorder.new(recorder_name: 'default').role_arn)
assert_nil(AwsConfigurationRecorder.new(recorder_name: 'NonExistentRecorder').role_arn)
end
def test_property_resource_types
assert_equal(['AWS::EC2::CustomerGateway', 'AWS::EC2::EIP'], AwsConfigurationRecorder.new(recorder_name: 'Recorder_2').resource_types)
assert_nil(AwsConfigurationRecorder.new(recorder_name: 'NonExistentRecorder').resource_types)
end
end
#=============================================================================#
# Test Matchers
#=============================================================================#
class AwsConfigurationRecorderPropertiesTest < Minitest::Test
def test_matcher_all_supported
assert AwsConfigurationRecorder.new(recorder_name: 'default').recording_all_resource_types?
refute AwsConfigurationRecorder.new(recorder_name: 'Recorder_1').recording_all_resource_types?
end
def test_matcher_has_include_global_resource_types
assert AwsConfigurationRecorder.new(recorder_name: 'default').recording_all_global_types?
refute AwsConfigurationRecorder.new(recorder_name: 'Recorder_1').recording_all_global_types?
end
def test_matcher_recording
assert AwsConfigurationRecorder.new(recorder_name: 'default').recording?
refute AwsConfigurationRecorder.new(recorder_name: 'Recorder_1').recording?
end
end
#=============================================================================#
# Test Fixtures
#=============================================================================#
module AwsMCRSB
class Basic < AwsBackendBase
def describe_configuration_recorders(query)
recorders = {
'default' => OpenStruct.new({
:configuration_recorders => [
name: "default",
role_arn: "arn:aws:iam::721741954427:role/default",
:recording_group => OpenStruct.new({
all_supported: true,
include_global_resource_types: true,
resource_types: []
}),
]
}),
'Recorder_2' => OpenStruct.new({
:configuration_recorders => [
name: "Recorder_2",
role_arn: "arn:aws:iam::721741954427:role/Recorder_1",
:recording_group => OpenStruct.new({
all_supported: false,
include_global_resource_types: false,
resource_types: ['AWS::EC2::CustomerGateway', 'AWS::EC2::EIP']
}),
]
}),
'empty' => {}
}
return recorders[query[:configuration_recorder_names][0]] unless recorders[query[:configuration_recorder_names][0]].nil?
recorders['empty']
end
def describe_configuration_recorder_status(query)
recorders = {
'default' => OpenStruct.new({
:configuration_recorders_status => [
recording: true,
]
}),
'Recorder_1' => OpenStruct.new({
:configuration_recorders_status => [
recording: false,
]
}),
'empty' => {}
}
return recorders[query[:configuration_recorder_names][0]] unless recorders[query[:configuration_recorder_names][0]].nil?
raise Aws::ConfigService::Errors::NoSuchConfigurationRecorderException(nil, nil)
end
end
end