Skeletal Resource: aws_vpc_subnet (#209)

Signed-off-by: Matthew Dromazos <dromazmj@dukes.jmu.edu>
This commit is contained in:
Matthew Dromazos 2018-02-07 10:03:11 -05:00 committed by Clinton Wolfe
parent b245b72a7c
commit 16fee68c88
6 changed files with 580 additions and 0 deletions

148
docs/aws_vpc_subnet.md Normal file
View file

@ -0,0 +1,148 @@
---
title: About the aws_vpc_subnet Resource
---
# aws_vpc_subnet
Use the `aws_vpc_subnet` InSpec audit resource to test properties of a vpc subnet.
To test properties of a single VPC subnet, use the `aws_vpc_subnet` resource.
To test properties of all or a group of VPC subnets, use the `aws_vpc_subnets` resource.
<br>
## Syntax
An `aws_vpc_subnet` resource block uses the parameter to select a VPC and a subnet in the VPC.
describe aws_vpc_subnet(vpc_id: 'vpc-01234567', subnet_id: 'subnet-1234567') do
it { should exist }
its('cidr_block') { should eq ['10.0.1.0/24'] }
end
<br>
## Resource Parameters
This InSpec resource accepts the following parameters, which are used to search for the VPCs subnet.
### vpc_id
A string identifying the VPC which contains zero or more subnets.
# This will error if there is more than the default SG
describe aws_vpc_subnet(vpc_id: 'vpc-12345678', 'subnet-1234567') do
it { should exist }
end
### subnet_id
A string identifying the subnet that the VPC contains.
# This will error if there is more than the default SG
describe aws_vpc_subnet(vpc_id: 'vpc-12345678', subnet_id: 'subnet-12345678') do
it { should exist }
end
<br>
## Properties
### assign_ipv_6_address_on_creation
Detects whether the network interface on the subnet accepts IPv6 addresses.
describe aws_vpc_subnet(vpc_id: 'vpc-12345678' , subnet_id: 'subnet-12345678') do
its('assign_ipv_6_address_on_creation') { should eq false }
end
### availability_zone
Provides the Availability Zone of the subnet.
describe aws_vpc_subnet(vpc_id: 'vpc-12345678' , subnet_id: 'subnet-12345678') do
its('availability_zone') { should eq 'us-east-1c' }
end
### available_ip_address_count
Provides the number of available IPv4 addresses on the subnet.
describe aws_vpc_subnet(vpc_id: 'vpc-12345678' , subnet_id: 'subnet-12345678') do
its('available_ip_address_count') { should eq 251 }
end
### cidr_block
Provides the block of ip addresses specified to the subnet.
describe aws_vpc_subnet(vpc_id: 'vpc-12345678' , subnet_id: 'subnet-12345678') do
its('cidr_block') { should eq '10.0.1.0/24' }
end
### default_for_az
Detects if this is the default subnet for the Availability Zone.
describe aws_vpc_subnet(vpc_id: 'vpc-12345678' , subnet_id: 'subnet-12345678') do
its('default_for_az') { should eq false }
end
### ipv_6_cidr_block_association_set
Provides information about the IPv6 cidr_block associatied with the subnet.
describe aws_vpc_subnet(vpc_id: 'vpc-12345678' , subnet_id: 'subnet-12345678') do
its('ipv_6_cidr_block_association_set') { should eq [
{
"Ipv6CidrBlock": "2001:db8:1234:a101::/64",
"AssociationId": "subnet-cidr-assoc-30e7e348",
"Ipv6CidrBlockState": {
"State": "ASSOCIATED"
}
}
] }
end
### map_public_ip_on_launch
Provides the ID of the VPC the subnet is in.
describe aws_vpc_subnet(vpc_id: 'vpc-12345678' , subnet_id: 'subnet-12345678') do
its('map_public_ip_on_launch') { should eq false }
end
### state
Provides the ID of the VPC the subnet is in.
describe aws_vpc_subnet(vpc_id: 'vpc-12345678' , subnet_id: 'subnet-12345678') do
its('state') { should eq 'available' }
end
### subnet_id
Provides the ID of the VPC the subnet is in.
describe aws_vpc_subnet(vpc_id: 'vpc-12345678' , subnet_id: 'subnet-12345678') do
its('subnet_id') { should eq 'subnet-12345678' }
end
### vpc_id
Provides the ID of the VPC the subnet is in.
describe aws_vpc_subnet(vpc_id: 'vpc-12345678' , subnet_id: 'subnet-12345678') do
its('vpc_id') { should eq 'vpc-12345678' }
end
## Matchers
### exist
The `exist` matcher indicates that a subnet exists for the specified vpc.
describe aws_vpc_subnet(vpc_id: 'vpc-1234567', subnet_id: 'subnet-12345678') do
it { should exist }
end

View file

@ -0,0 +1,123 @@
---
title: About the aws_vpc_subnet Resource
---
# aws_vpc_subnet
Use the `aws_vpc_subnet` InSpec audit resource to test properties of a vpc subnet.
To test properties of a single VPC subnet, use the `aws_vpc_subnet` resource.
To test properties of all or a group of VPC subnets, use the `aws_vpc_subnets` resource.
<br>
## Syntax
An `aws_vpc_subnet` resource block uses the parameter to select a VPC and a subnet in the VPC.
describe aws_vpc_subnet(subnet_id: 'subnet-1234567') do
it { should exist }
its('cidr_block') { should eq '10.0.1.0/24' }
end
<br>
## Resource Parameters
This InSpec resource accepts the following parameters, which are used to search for the VPCs subnet.
### subnet_id
A string identifying the subnet that the VPC contains.
# This will error if there is more than the default SG
describe aws_vpc_subnet(subnet_id: 'subnet-12345678') do
it { should exist }
end
<br>
## Matchers
### assigning_ipv_6_address_on_creation
Detects whether the network interface on the subnet accepts IPv6 addresses.
describe aws_vpc_subnet(subnet_id: 'subnet-12345678') do
it { should be_assigning_ipv_6_address_on_creation }
end
### available
Provides the current state of the subnet.
describe aws_vpc_subnet(subnet_id: 'subnet-12345678') do
it { should be_available }
end
### default_for_az
Detects if this is the default subnet for the Availability Zone.
describe aws_vpc_subnet(subnet_id: 'subnet-12345678') do
it { should be_default_for_az }
end
### exist
The `exist` matcher indicates that a subnet exists for the specified vpc.
describe aws_vpc_subnet(subnet_id: 'subnet-12345678') do
it { should exist }
end
### mapping_public_ip_on_launch
Provides the ID of the VPC the subnet is in.
describe aws_vpc_subnet(subnet_id: 'subnet-12345678') do
it { should be_mapping_public_ip_on_launch }
end
## Properties
### availability_zone
Provides the Availability Zone of the subnet.
describe aws_vpc_subnet(subnet_id: 'subnet-12345678') do
its('availability_zone') { should eq 'us-east-1c' }
end
### available_ip_address_count
Provides the number of available IPv4 addresses on the subnet.
describe aws_vpc_subnet(subnet_id: 'subnet-12345678') do
its('available_ip_address_count') { should eq 251 }
end
### cidr_block
Provides the block of ip addresses specified to the subnet.
describe aws_vpc_subnet(subnet_id: 'subnet-12345678') do
its('cidr_block') { should eq '10.0.1.0/24' }
end
### subnet_id
Provides the ID of the Subnet.
describe aws_vpc_subnet(subnet_id: 'subnet-12345678') do
its('subnet_id') { should eq 'subnet-12345678' }
end
### vpc_id
Provides the ID of the VPC the subnet is in.
describe aws_vpc_subnet(subnet_id: 'subnet-12345678') do
its('vpc_id') { should eq 'vpc-12345678' }
end

View file

@ -0,0 +1,89 @@
# author: Matthew Dromazos
require '_aws'
class AwsVpcSubnet < Inspec.resource(1)
name 'aws_vpc_subnet'
desc 'This resource is used to test the attributes of a VPC subnet'
example "
describe aws_vpc_subnet(subnet_id: 'subnet-12345678') do
it { should exist }
its('cidr_block') { should eq '10.0.1.0/24' }
end
"
include AwsResourceMixin
attr_reader :vpc_id, :subnet_id, :cidr_block, :availability_zone, :available_ip_address_count,
:default_for_az, :mapping_public_ip_on_launch, :available, :ipv_6_cidr_block_association_set,
:assigning_ipv_6_address_on_creation
alias available? available
alias default_for_az? default_for_az
alias mapping_public_ip_on_launch? mapping_public_ip_on_launch
alias assigning_ipv_6_address_on_creation? assigning_ipv_6_address_on_creation
def to_s
"VPC Subnet #{@subnet_id}"
end
private
def validate_params(raw_params)
validated_params = check_resource_param_names(
raw_params: raw_params,
allowed_params: [:subnet_id],
allowed_scalar_name: :subnet_id,
allowed_scalar_type: String,
)
# Make sure the subnet_id parameter was specified and in the correct form.
if validated_params.key?(:subnet_id) && validated_params[:subnet_id] !~ /^subnet\-[0-9a-f]{8}/
raise ArgumentError, 'aws_vpc_subnet Subnet ID must be in the format "subnet-" followed by 8 hexadecimal characters.'
end
if validated_params.empty?
raise ArgumentError, 'You must provide a subnet_id to aws_vpc_subnet.'
end
validated_params
end
def fetch_from_aws
backend = AwsVpcSubnet::BackendFactory.create
# Transform into filter format expected by AWS
filters = []
filters.push({ name: 'subnet-id', values: [@subnet_id] })
ds_response = backend.describe_subnets(filters: filters)
# If no subnets exist in the VPC, exist is false.
if ds_response.subnets.empty?
@exists = false
return
end
@exists = true
assign_properties(ds_response)
end
def assign_properties(ds_response)
@vpc_id = ds_response.subnets[0].vpc_id
@subnet_id = ds_response.subnets[0].subnet_id
@cidr_block = ds_response.subnets[0].cidr_block
@availability_zone = ds_response.subnets[0].availability_zone
@available_ip_address_count = ds_response.subnets[0].available_ip_address_count
@default_for_az = ds_response.subnets[0].default_for_az
@mapping_public_ip_on_launch = ds_response.subnets[0].map_public_ip_on_launch
@available = ds_response.subnets[0].state == 'available'
@ipv_6_cidr_block_association_set = ds_response.subnets[0].ipv_6_cidr_block_association_set
@assigning_ipv_6_address_on_creation = ds_response.subnets[0].assign_ipv_6_address_on_creation
end
# Uses the SDK API to really talk to AWS
class Backend
class AwsClientApi
BackendFactory.set_default_backend(self)
def describe_subnets(query)
AWSConnection.new.ec2_client.describe_subnets(query)
end
end
end
end

View file

@ -198,6 +198,19 @@ output "ec2_security_group_alpha_group_id" {
value = "${aws_security_group.alpha.id}"
}
#============================================================#
# VPC Subnets
#============================================================#
resource "aws_subnet" "subnet_01" {
vpc_id = "${data.aws_vpc.default.id}"
cidr_block = "172.31.96.0/20"
}
output "ec2_default_vpc_subnet_01_id" {
value = "${aws_subnet.subnet_01.id}"
}
output "ec2_security_group_alpha_group_name" {
value = "${aws_security_group.alpha.name}"
}

View file

@ -0,0 +1,47 @@
fixtures = {}
[
'ec2_security_group_default_vpc_id',
'ec2_default_vpc_subnet_01_id',
].each do |fixture_name|
fixtures[fixture_name] = attribute(
fixture_name,
default: "default.#{fixture_name}",
description: 'See ../build/ec2.tf',
)
end
control "aws_vpc_subnet recall of subnet_01" do
# Test hash given subnet_id
describe aws_vpc_subnet(subnet_id: fixtures['ec2_default_vpc_subnet_01_id']) do
it { should exist }
end
# Test scalar works
describe aws_vpc_subnet(fixtures['ec2_default_vpc_subnet_01_id']) do
it { should exist }
end
describe aws_vpc_subnet(subnet_id: 'subnet-00000000') do
it { should_not exist }
end
end
control "aws_vpc_subnet properties of subnet_01" do
describe aws_vpc_subnet(subnet_id: fixtures['ec2_default_vpc_subnet_01_id']) do
its('vpc_id') { should eq fixtures['ec2_security_group_default_vpc_id'] }
its('subnet_id') { should eq fixtures['ec2_default_vpc_subnet_01_id'] }
its('cidr_block') { should eq '172.31.96.0/20' }
its('available_ip_address_count') { should eq 4091 }
its('availability_zone') { should eq 'us-east-1c' }
its('ipv_6_cidr_block_association_set') { should eq [] }
end
end
control "aws_vpc_subnet matchers of subnet_01" do
describe aws_vpc_subnet(subnet_id: fixtures['ec2_default_vpc_subnet_01_id']) do
it { should be_available }
it { should_not be_mapping_public_ip_on_launch }
it { should_not be_default_for_az }
it { should_not be_assigning_ipv_6_address_on_creation }
end
end

View file

@ -0,0 +1,160 @@
# encoding: utf-8
require 'helper'
require 'aws_vpc_subnet'
# MVSSB = MockVpcSubnetSingleBackend
# Abbreviation not used outside this file
#=============================================================================#
# Constructor Tests
#=============================================================================#
class AwsVpcSubnetConstructorTest < Minitest::Test
def setup
AwsVpcSubnet::BackendFactory.select(AwsMVSSB::Basic)
end
def test_constructor_no_args_raises
assert_raises(ArgumentError) { AwsVpcSubnet.new }
end
def test_constructor_expected_well_formed_args
AwsVpcSubnet.new(subnet_id: 'subnet-12345678')
end
def test_constructor_reject_unknown_resource_params
assert_raises(ArgumentError) { AwsVpcSubnet.new(bla: 'blabla') }
end
end
#=============================================================================#
# Recall
#=============================================================================#
class AwsVpcSubnetRecallTest < Minitest::Test
def setup
AwsVpcSubnet::BackendFactory.select(AwsMVSSB::Basic)
end
def test_search_hit_via_hash_with_vpc_id_and_subnet_id_works
assert AwsVpcSubnet.new(subnet_id: 'subnet-12345678').exists?
end
def test_search_miss_is_not_an_exception
refute AwsVpcSubnet.new(subnet_id: 'subnet-00000000').exists?
end
end
#=============================================================================#
# properties
#=============================================================================#
class AwsVpcSubnetPropertiesTest < Minitest::Test
def setup
AwsVpcSubnet::BackendFactory.select(AwsMVSSB::Basic)
end
def test_property_subnet_id
assert_equal('subnet-12345678', AwsVpcSubnet.new(subnet_id: 'subnet-12345678').subnet_id)
end
def test_property_vpc_id
assert_equal('vpc-12345678', AwsVpcSubnet.new(subnet_id: 'subnet-12345678').vpc_id)
end
def test_property_cidr_block
assert_equal('10.0.1.0/24', AwsVpcSubnet.new(subnet_id: 'subnet-12345678').cidr_block)
assert_nil(AwsVpcSubnet.new(subnet_id: 'subnet-00000000').cidr_block)
end
def test_property_availability_zone
assert_equal('us-east-1', AwsVpcSubnet.new(subnet_id: 'subnet-12345678').availability_zone)
assert_nil(AwsVpcSubnet.new(subnet_id: 'subnet-00000000').availability_zone)
end
def test_property_available_ip_address_count
assert_equal(251, AwsVpcSubnet.new(subnet_id: 'subnet-12345678').available_ip_address_count)
assert_nil(AwsVpcSubnet.new(subnet_id: 'subnet-00000000').available_ip_address_count)
end
def test_property_ipv_6_cidr_block_association_set
assert_equal([], AwsVpcSubnet.new(subnet_id: 'subnet-12345678').ipv_6_cidr_block_association_set)
assert_nil(AwsVpcSubnet.new(subnet_id: 'subnet-00000000').ipv_6_cidr_block_association_set)
end
end
#=============================================================================#
# Test Matchers
#=============================================================================#
class AwsVpcSubnetPropertiesTest < Minitest::Test
def test_matcher_assign_ipv_6_address_on_creation
assert AwsVpcSubnet.new(subnet_id: 'subnet-12345678').assigning_ipv_6_address_on_creation
refute AwsVpcSubnet.new(subnet_id: 'subnet-87654321').assigning_ipv_6_address_on_creation
end
def test_matcher_available
assert AwsVpcSubnet.new(subnet_id: 'subnet-12345678').available?
refute AwsVpcSubnet.new(subnet_id: 'subnet-87654321').available?
end
def test_matcher_default_for_az
assert AwsVpcSubnet.new(subnet_id: 'subnet-12345678').default_for_az?
refute AwsVpcSubnet.new(subnet_id: 'subnet-87654321').default_for_az?
end
def test_matcher_map_public_ip_on_launch
assert AwsVpcSubnet.new(subnet_id: 'subnet-12345678').mapping_public_ip_on_launch
refute AwsVpcSubnet.new(subnet_id: 'subnet-87654321').mapping_public_ip_on_launch
end
end
#=============================================================================#
# Test Fixtures
#=============================================================================#
module AwsMVSSB
class Basic < AwsVpcSubnet::Backend
def describe_subnets(query)
subnets = {
'subnet-12345678' => OpenStruct.new({
:subnets => [
OpenStruct.new({
availability_zone: "us-east-1",
available_ip_address_count: 251,
cidr_block: "10.0.1.0/24",
default_for_az: true,
map_public_ip_on_launch: true,
state: "available",
subnet_id: "subnet-12345678",
vpc_id: "vpc-12345678",
ipv_6_cidr_block_association_set: [],
assign_ipv_6_address_on_creation: true,
}),
]
}),
'subnet-87654321' => OpenStruct.new({
:subnets => [
OpenStruct.new({
availability_zone: "us-east-1",
available_ip_address_count: 251,
cidr_block: "10.0.1.0/24",
default_for_az: false,
map_public_ip_on_launch: false,
state: "pending",
subnet_id: "subnet-87654321",
vpc_id: "vpc-87654321",
ipv_6_cidr_block_association_set: [],
assign_ipv_6_address_on_creation: false,
}),
]
}),
'empty' => OpenStruct.new({
:subnets => []
})
}
return subnets[query[:filters][0][:values][0]] unless subnets[query[:filters][0][:values][0]].nil?
subnets['empty']
end
end
end