2019-06-11 22:24:35 +00:00
require " resource_support/aws/aws_singular_resource_mixin "
require " resource_support/aws/aws_backend_base "
require " aws-sdk-ec2 "
2019-05-25 08:33:26 +00:00
2017-10-11 20:18:20 +00:00
class AwsEc2Instance < Inspec . resource ( 1 )
2019-06-11 22:24:35 +00:00
name " aws_ec2_instance "
desc " Verifies settings for an EC2 instance "
2016-12-15 08:53:01 +00:00
2019-03-19 14:17:32 +00:00
example << ~ EXAMPLE
2017-10-17 12:38:49 +00:00
describe aws_ec2_instance ( 'i-123456' ) do
2016-12-15 08:53:01 +00:00
it { should be_running }
2017-10-26 19:56:32 +00:00
it { should have_roles }
2016-12-15 08:53:01 +00:00
end
2017-10-17 12:38:49 +00:00
describe aws_ec2_instance ( name : 'my-instance' ) do
2016-12-15 08:53:01 +00:00
it { should be_running }
2017-10-26 19:56:32 +00:00
it { should have_roles }
2016-12-15 08:53:01 +00:00
end
2019-03-19 14:17:32 +00:00
EXAMPLE
2019-06-11 22:24:35 +00:00
supports platform : " aws "
2016-12-15 08:53:01 +00:00
2018-02-08 04:26:37 +00:00
# TODO: rewrite to avoid direct injection, match other resources, use AwsSingularResourceMixin
def initialize ( opts , conn = nil )
2016-12-15 08:53:01 +00:00
@opts = opts
@opts . is_a? ( Hash ) ? @display_name = @opts [ :name ] : @display_name = opts
2018-02-08 04:26:37 +00:00
@ec2_client = conn ? conn . ec2_client : inspec_runner . backend . aws_client ( Aws :: EC2 :: Client )
@ec2_resource = conn ? conn . ec2_resource : inspec_runner . backend . aws_resource ( Aws :: EC2 :: Resource , { } )
@iam_resource = conn ? conn . iam_resource : inspec_runner . backend . aws_resource ( Aws :: IAM :: Resource , { } )
end
2018-02-14 19:15:20 +00:00
# TODO: DRY up, see https://github.com/chef/inspec/issues/2633
# Copied from resource_support/aws/aws_resource_mixin.rb
def catch_aws_errors
yield
rescue Aws :: Errors :: MissingCredentialsError
# The AWS error here is unhelpful:
# "unable to sign request without credentials set"
Inspec :: Log . error " It appears that you have not set your AWS credentials. You may set them using environment variables, or using the 'aws://region/aws_credentials_profile' target. See https://www.inspec.io/docs/reference/platforms for details. "
2019-06-11 22:24:35 +00:00
fail_resource ( " No AWS credentials available " )
2018-02-14 19:15:20 +00:00
rescue Aws :: Errors :: ServiceError = > e
fail_resource e . message
end
# TODO: DRY up, see https://github.com/chef/inspec/issues/2633
# Copied from resource_support/aws/aws_singular_resource_mixin.rb
2018-02-08 04:26:37 +00:00
def inspec_runner
# When running under inspec-cli, we have an 'inspec' method that
# returns the runner. When running under unit tests, we don't
# have that, but we still have to call this to pass something
# (nil is OK) to the backend.
# TODO: remove with https://github.com/chef/inspec-aws/issues/216
# TODO: remove after rewrite to include AwsSingularResource
inspec if respond_to? ( :inspec )
2016-12-15 08:53:01 +00:00
end
def id
return @instance_id if defined? ( @instance_id )
2019-07-09 00:20:30 +00:00
2018-02-14 19:15:20 +00:00
catch_aws_errors do
if @opts . is_a? ( Hash )
first = @ec2_resource . instances (
{
filters : [ {
2019-06-11 22:24:35 +00:00
name : " tag:Name " ,
2018-02-14 19:15:20 +00:00
values : [ @opts [ :name ] ] ,
} ] ,
2019-06-11 22:24:35 +00:00
}
2018-02-14 19:15:20 +00:00
) . first
# catch case where the instance is not known
@instance_id = first . id unless first . nil?
else
@instance_id = @opts
end
2016-12-15 08:53:01 +00:00
end
end
alias instance_id id
def exists?
2017-10-11 20:18:20 +00:00
return false if instance . nil?
2019-07-09 00:20:30 +00:00
2017-02-09 21:31:24 +00:00
instance . exists?
2016-12-15 08:53:01 +00:00
end
# returns the instance state
def state
2018-02-14 19:15:20 +00:00
catch_aws_errors do
instance & . state & . name
end
2016-12-15 08:53:01 +00:00
end
# helper methods for each state
%w{
pending running shutting - down
terminated stopping stopped unknown
} . each do | state_name |
2019-06-11 22:24:35 +00:00
define_method state_name . tr ( " - " , " _ " ) + " ? " do
2016-12-15 08:53:01 +00:00
state == state_name
end
end
# attributes that we want to expose
%w{
public_ip_address private_ip_address key_name private_dns_name
public_dns_name subnet_id architecture root_device_type
root_device_name virtualization_type client_token launch_time
instance_type image_id vpc_id
} . each do | attribute |
define_method attribute do
2018-02-14 19:15:20 +00:00
catch_aws_errors do
instance . send ( attribute ) if instance
end
2016-12-15 08:53:01 +00:00
end
end
2018-02-14 20:08:34 +00:00
# Don't document this - it's a bit hard to use. Our current doctrine
# is to use dumb things, like arrays of strings - use security_group_ids instead.
2016-12-15 08:53:01 +00:00
def security_groups
2018-02-14 19:15:20 +00:00
catch_aws_errors do
2019-06-11 22:24:35 +00:00
@security_groups || = instance . security_groups . map do | sg |
2018-02-14 19:15:20 +00:00
{ id : sg . group_id , name : sg . group_name }
2019-06-11 22:24:35 +00:00
end
2018-02-14 19:15:20 +00:00
end
2016-12-15 08:53:01 +00:00
end
2018-02-14 20:08:34 +00:00
def security_group_ids
catch_aws_errors do
@security_group_ids || = instance . security_groups . map ( & :group_id )
end
end
2016-12-15 08:53:01 +00:00
def tags
2018-02-14 19:15:20 +00:00
catch_aws_errors do
@tags || = instance . tags . map { | tag | { key : tag . key , value : tag . value } }
end
2016-12-15 08:53:01 +00:00
end
def to_s
" EC2 Instance #{ @display_name } "
end
2017-10-26 19:56:32 +00:00
def has_roles?
2018-02-14 19:15:20 +00:00
catch_aws_errors do
instance_profile = instance . iam_instance_profile
if instance_profile
roles = @iam_resource . instance_profile (
2019-06-11 22:24:35 +00:00
instance_profile . arn . gsub ( %r{ ^.* \ / } , " " )
2018-02-14 19:15:20 +00:00
) . roles
else
roles = nil
end
roles && ! roles . empty?
2017-10-26 19:56:32 +00:00
end
end
2016-12-15 08:53:01 +00:00
private
def instance
2018-02-14 19:15:20 +00:00
catch_aws_errors { @instance || = @ec2_resource . instance ( id ) }
2016-12-15 08:53:01 +00:00
end
end