Skip to content

Commit

Permalink
New Skeletal Resource aws_config_delivery_channel (#2641)
Browse files Browse the repository at this point in the history
* Initial commit of new skeletal resource aws_config_delivery_channel
* Changes delivery_frequency to be an integer and names delivery_frequency_in_hours
* Adds more documentation and clarifies descriptions
* Wraps API call in the aws_catch_errors function
* Changes config bucket name to use dashes instead of underscores
* Updates on master and changes directory location of build and integration files
* Fix integration tests to only create one ConfigRecorder

Signed-off-by: Matthew Dromazos <dromazmj@dukes.jmu.edu>
  • Loading branch information
dromazmj authored and jquick committed Mar 26, 2018
1 parent afbb366 commit 0cbe5b6
Show file tree
Hide file tree
Showing 5 changed files with 282 additions and 1 deletion.
79 changes: 79 additions & 0 deletions docs/resources/aws_config_delivery_channel.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
---
title: About the aws_config_delivery_channel Resource
---

# aws_config_delivery_channel

The AWS Config service can monitor and record changes to your AWS resource configurations. A Delivery Channel can record the changes
to an S3 Bucket, an SNS or both.

Use the `aws_config_delivery_channel` InSpec audit resource to examine how the AWS Config service delivers those change notifications.

<br>

## Syntax

An `aws_config_delivery_channel` resource block declares the tests for a single AWS Config delivery channel.

describe aws_config_delivery_channel('my_channel') do
it { should exist }
end

describe aws_config_delivery_channel(channel_name: 'my-channel') do
it { should exist }
end

<br>

## Examples

The following examples show how to use this InSpec audit resource.

### Test how frequent the channel writes configuration changes to the s3 bucket.

describe aws_config_delivery_channel(channel_name: 'my-recorder') do
its(delivery_frequency_in_hours) { should be > 3 }
end

## Properties

### s3_bucket_name

Provides the name of the s3 bucket that the channel sends configuration changes to. This is an optional value since a Delivery Channel can also talk to an SNS.

describe aws_config_delivery_channel(channel_name: 'my_channel')
its('s3_bucket_name') { should eq 'my_bucket' }
end

### s3_key_prefix

Provides the s3 object key prefix (or "path") under which configuration data will be recorded.

describe aws_config_delivery_channel(channel_name: 'my_channel')
its('s3_key_prefix') { should eq 'log/' }
end

### sns_topic_arn

Provides the ARN of the SNS topic for which the channel sends notifications about configuration changes.

describe aws_config_delivery_channel(channel_name: 'my_channel')
its('sns_topic_arn') { should eq 'arn:aws:sns:us-east-1:721741954427:sns_topic' }
end

### delivery_frequency_in_hours

Provides how often the AWS Config sends configuration changes to the s3 bucket in the delivery channel.

describe aws_config_delivery_channel(channel_name: 'my_channel')
its('delivery_frequency_in_hours') { should eq 24 }
its('delivery_frequency_in_hours') { should be > 24 }
end


<br>

## Matchers

This resource provides no matchers, aside from the standard exists matcher.

1 change: 1 addition & 0 deletions lib/resource_support/aws.rb
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
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_delivery_channel'
require 'resources/aws/aws_config_recorder'
require 'resources/aws/aws_ec2_instance'
require 'resources/aws/aws_iam_access_key'
Expand Down
76 changes: 76 additions & 0 deletions lib/resources/aws/aws_config_delivery_channel.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
class AwsConfigDeliveryChannel < Inspec.resource(1)
name 'aws_config_delivery_channel'
desc 'Verifies settings for AWS Config Delivery Channel'
example "
describe aws_config_delivery_channel do
it { should exist }
its('s3_bucket_name') { should eq 'my_bucket' }
its('sns_topic_arn') { should eq arn:aws:sns:us-east-1:721741954427:sns_topic' }
end
"
supports platform: 'aws'

include AwsSingularResourceMixin
attr_reader :channel_name, :s3_bucket_name, :s3_key_prefix, :sns_topic_arn,
:delivery_frequency_in_hours

def to_s
"Config_Delivery_Channel: #{@channel_name}"
end

private

def validate_params(raw_params)
validated_params = check_resource_param_names(
raw_params: raw_params,
allowed_params: [:channel_name],
allowed_scalar_name: :channel_name,
allowed_scalar_type: String,
)

# Make sure channel_name is given as param
if validated_params[:channel_name].nil?
raise ArgumentError, 'You must provide a channel_name to aws_config_delivery_channel'
end

validated_params
end

def fetch_from_api
backend = BackendFactory.create(inspec_runner)
query = { delivery_channel_names: [@channel_name] }
catch_aws_errors do
@resp = backend.describe_delivery_channels(query)
end
@exists = !@resp.empty?
return unless @exists

@channel = @resp.delivery_channels.first.to_h
@channel_name = @channel[:name]
@s3_bucket_name = @channel[:s3_bucket_name]
@s3_key_prefix = @channel[:s3_key_prefix]
@sns_topic_arn = @channel[:sns_topic_arn]
@delivery_frequency_in_hours = @channel[:config_snapshot_delivery_properties][:delivery_frequency] unless @channel[:config_snapshot_delivery_properties].nil?
frequencies = {
'One_Hour' => 1,
'TwentyFour_Hours' => 24,
'Three_Hours' => 3,
'Six_Hours' => 6,
'Twelve_Hours' => 12,
}
@delivery_frequency_in_hours = frequencies[@delivery_frequency_in_hours]
end

class Backend
class AwsClientApi < AwsBackendBase
BackendFactory.set_default_backend(self)
self.aws_client_class = Aws::ConfigService::Client

def describe_delivery_channels(query)
aws_service_client.describe_delivery_channels(query)
rescue Aws::ConfigService::Errors::NoSuchDeliveryChannelException
return {}
end
end
end
end
79 changes: 78 additions & 1 deletion test/integration/aws/default/build/config.tf
Original file line number Diff line number Diff line change
Expand Up @@ -33,4 +33,81 @@ output "role_for_config_recorder_arn" {

output "config_recorder_name" {
value = "${aws_config_configuration_recorder.config_recorder.name}"
}
}

#======================================================#
# Configuration Delivery Channel
#======================================================#

# Note that since AWS accounts can only have one Config Recorder,
# we have to re-use it here (as well as its role).

resource "aws_config_delivery_channel" "delivery_channel_01" {
name = "delivery_channel_01"
s3_bucket_name = "${aws_s3_bucket.bucket_for_delivery_channel.bucket}"
depends_on = ["aws_config_configuration_recorder.config_recorder"]
sns_topic_arn = "${aws_sns_topic.sns_topic_for_delivery_channel.arn}"
snapshot_delivery_properties = {
delivery_frequency = "TwentyFour_Hours"
}
}


output "delivery_channel_01" {
value = "${aws_config_delivery_channel.delivery_channel_01.id}"
}

output "config_recorder_for_delivery_channel_role_arn" {
value = "${aws_iam_role.role_for_config_recorder.arn}"
}

#======================================================#
# IAM Roles
#======================================================#

resource "aws_iam_role_policy" "policy_for_delivery_channel" {
name = "policy_for_delivery_channel"
role = "${aws_iam_role.role_for_config_recorder.id}"

policy = <<POLICY
{
"Version": "2012-10-17",
"Statement": [
{
"Action": [
"s3:*"
],
"Effect": "Allow",
"Resource": [
"${aws_s3_bucket.bucket_for_delivery_channel.arn}",
"${aws_s3_bucket.bucket_for_delivery_channel.arn}/*"
]
}
]
}
POLICY
}

#=================================================================#
# Config S3 Buckets
#=================================================================#
resource "aws_s3_bucket" "bucket_for_delivery_channel" {
bucket = "inspec-bucket-for-delivery-channel-${terraform.env}.chef.io"
acl = "public-read"
force_destroy = true
}

output "s3_bucket_for_delivery_channel" {
value = "${aws_s3_bucket.bucket_for_delivery_channel.id}"
}

#===========================================================================#
# SNS Topic
#===========================================================================#
resource "aws_sns_topic" "sns_topic_for_delivery_channel" {
name = "${terraform.env}-sns_topic_for_delivery_channel"
}

output "sns_topic_for_delivery_channel_arn" {
value = "${aws_sns_topic.sns_topic_for_delivery_channel.arn}"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
fixtures = {}
[
'delivery_channel_01',
'config_recorder_for_delivery_channel_role_arn',
's3_bucket_for_delivery_channel',
'delivery_channel_01_bucket_prefix',
'sns_topic_for_delivery_channel_arn'
].each do |fixture_name|
fixtures[fixture_name] = attribute(
fixture_name,
default: "default.#{fixture_name}",
description: 'See ../build/iam.tf',
)
end

#======================================================#
# aws_config_delivery_channel - Singular
#======================================================#

#------------------- Recall / Miss -------------------#

control "aws_config_delivery_channel recall" do
# Test scalar param
describe aws_config_delivery_channel(fixtures['delivery_channel_01']) do
it { should exist }
end

# Test hash parameter
describe aws_config_delivery_channel(channel_name: fixtures['delivery_channel_01']) do
it { should exist }
end

# Test recorder that doesnt exist
describe aws_config_delivery_channel(channel_name: 'NonExistentChannel') do
it { should_not exist }
end
end

#------------------- Properties -------------------#
control "aws_config_delivery_channel properties" do
describe aws_config_delivery_channel(fixtures['delivery_channel_01']) do
its('s3_bucket_name') { should eq fixtures['s3_bucket_for_delivery_channel'] }
its('s3_key_prefix') { should eq nil }
its('sns_topic_arn') { should eq fixtures['sns_topic_for_delivery_channel_arn'] }
its('delivery_frequency_in_hours') { should eq 24 }
its('delivery_frequency_in_hours') { should be > 3 }
end
end

0 comments on commit 0cbe5b6

Please sign in to comment.