386 lines
17 KiB
Python
386 lines
17 KiB
Python
|
# Copyright (c) 2015 Amazon.com, Inc. or its affiliates. All Rights Reserved
|
||
|
#
|
||
|
# Permission is hereby granted, free of charge, to any person obtaining a
|
||
|
# copy of this software and associated documentation files (the
|
||
|
# "Software"), to deal in the Software without restriction, including
|
||
|
# without limitation the rights to use, copy, modify, merge, publish, dis-
|
||
|
# tribute, sublicense, and/or sell copies of the Software, and to permit
|
||
|
# persons to whom the Software is furnished to do so, subject to the fol-
|
||
|
# lowing conditions:
|
||
|
#
|
||
|
# The above copyright notice and this permission notice shall be included
|
||
|
# in all copies or substantial portions of the Software.
|
||
|
#
|
||
|
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||
|
# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABIL-
|
||
|
# ITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
|
||
|
# SHALL THE AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||
|
# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||
|
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||
|
# IN THE SOFTWARE.
|
||
|
#
|
||
|
|
||
|
import boto
|
||
|
from boto.compat import json
|
||
|
from boto.connection import AWSQueryConnection
|
||
|
from boto.regioninfo import RegionInfo
|
||
|
from boto.exception import JSONResponseError
|
||
|
from boto.configservice import exceptions
|
||
|
|
||
|
|
||
|
class ConfigServiceConnection(AWSQueryConnection):
|
||
|
"""
|
||
|
AWS Config
|
||
|
AWS Config provides a way to keep track of the configurations of
|
||
|
all the AWS resources associated with your AWS account. You can
|
||
|
use AWS Config to get the current and historical configurations of
|
||
|
each AWS resource and also to get information about the
|
||
|
relationship between the resources. An AWS resource can be an
|
||
|
Amazon Compute Cloud (Amazon EC2) instance, an Elastic Block Store
|
||
|
(EBS) volume, an Elastic network Interface (ENI), or a security
|
||
|
group. For a complete list of resources currently supported by AWS
|
||
|
Config, see `Supported AWS Resources`_.
|
||
|
|
||
|
You can access and manage AWS Config through the AWS Management
|
||
|
Console, the AWS Command Line Interface (AWS CLI), the AWS Config
|
||
|
API, or the AWS SDKs for AWS Config
|
||
|
|
||
|
This reference guide contains documentation for the AWS Config API
|
||
|
and the AWS CLI commands that you can use to manage AWS Config.
|
||
|
|
||
|
The AWS Config API uses the Signature Version 4 protocol for
|
||
|
signing requests. For more information about how to sign a request
|
||
|
with this protocol, see `Signature Version 4 Signing Process`_.
|
||
|
|
||
|
For detailed information about AWS Config features and their
|
||
|
associated actions or commands, as well as how to work with AWS
|
||
|
Management Console, see `What Is AWS Config?`_ in the AWS Config
|
||
|
Developer Guide .
|
||
|
"""
|
||
|
APIVersion = "2014-11-12"
|
||
|
DefaultRegionName = "us-east-1"
|
||
|
DefaultRegionEndpoint = "config.us-east-1.amazonaws.com"
|
||
|
ServiceName = "ConfigService"
|
||
|
TargetPrefix = "StarlingDoveService"
|
||
|
ResponseError = JSONResponseError
|
||
|
|
||
|
_faults = {
|
||
|
"InvalidLimitException": exceptions.InvalidLimitException,
|
||
|
"NoSuchBucketException": exceptions.NoSuchBucketException,
|
||
|
"InvalidSNSTopicARNException": exceptions.InvalidSNSTopicARNException,
|
||
|
"ResourceNotDiscoveredException": exceptions.ResourceNotDiscoveredException,
|
||
|
"MaxNumberOfDeliveryChannelsExceededException": exceptions.MaxNumberOfDeliveryChannelsExceededException,
|
||
|
"LastDeliveryChannelDeleteFailedException": exceptions.LastDeliveryChannelDeleteFailedException,
|
||
|
"InsufficientDeliveryPolicyException": exceptions.InsufficientDeliveryPolicyException,
|
||
|
"InvalidRoleException": exceptions.InvalidRoleException,
|
||
|
"InvalidTimeRangeException": exceptions.InvalidTimeRangeException,
|
||
|
"NoSuchDeliveryChannelException": exceptions.NoSuchDeliveryChannelException,
|
||
|
"NoSuchConfigurationRecorderException": exceptions.NoSuchConfigurationRecorderException,
|
||
|
"InvalidS3KeyPrefixException": exceptions.InvalidS3KeyPrefixException,
|
||
|
"InvalidDeliveryChannelNameException": exceptions.InvalidDeliveryChannelNameException,
|
||
|
"NoRunningConfigurationRecorderException": exceptions.NoRunningConfigurationRecorderException,
|
||
|
"ValidationException": exceptions.ValidationException,
|
||
|
"NoAvailableConfigurationRecorderException": exceptions.NoAvailableConfigurationRecorderException,
|
||
|
"InvalidNextTokenException": exceptions.InvalidNextTokenException,
|
||
|
"InvalidConfigurationRecorderNameException": exceptions.InvalidConfigurationRecorderNameException,
|
||
|
"NoAvailableDeliveryChannelException": exceptions.NoAvailableDeliveryChannelException,
|
||
|
"MaxNumberOfConfigurationRecordersExceededException": exceptions.MaxNumberOfConfigurationRecordersExceededException,
|
||
|
}
|
||
|
|
||
|
|
||
|
def __init__(self, **kwargs):
|
||
|
region = kwargs.pop('region', None)
|
||
|
if not region:
|
||
|
region = RegionInfo(self, self.DefaultRegionName,
|
||
|
self.DefaultRegionEndpoint)
|
||
|
|
||
|
if 'host' not in kwargs or kwargs['host'] is None:
|
||
|
kwargs['host'] = region.endpoint
|
||
|
|
||
|
super(ConfigServiceConnection, self).__init__(**kwargs)
|
||
|
self.region = region
|
||
|
|
||
|
def _required_auth_capability(self):
|
||
|
return ['hmac-v4']
|
||
|
|
||
|
def delete_delivery_channel(self, delivery_channel_name):
|
||
|
"""
|
||
|
Deletes the specified delivery channel.
|
||
|
|
||
|
The delivery channel cannot be deleted if it is the only
|
||
|
delivery channel and the configuration recorder is still
|
||
|
running. To delete the delivery channel, stop the running
|
||
|
configuration recorder using the StopConfigurationRecorder
|
||
|
action.
|
||
|
|
||
|
:type delivery_channel_name: string
|
||
|
:param delivery_channel_name: The name of the delivery channel to
|
||
|
delete.
|
||
|
|
||
|
"""
|
||
|
params = {'DeliveryChannelName': delivery_channel_name, }
|
||
|
return self.make_request(action='DeleteDeliveryChannel',
|
||
|
body=json.dumps(params))
|
||
|
|
||
|
def deliver_config_snapshot(self, delivery_channel_name):
|
||
|
"""
|
||
|
Schedules delivery of a configuration snapshot to the Amazon
|
||
|
S3 bucket in the specified delivery channel. After the
|
||
|
delivery has started, AWS Config sends following notifications
|
||
|
using an Amazon SNS topic that you have specified.
|
||
|
|
||
|
|
||
|
+ Notification of starting the delivery.
|
||
|
+ Notification of delivery completed, if the delivery was
|
||
|
successfully completed.
|
||
|
+ Notification of delivery failure, if the delivery failed to
|
||
|
complete.
|
||
|
|
||
|
:type delivery_channel_name: string
|
||
|
:param delivery_channel_name: The name of the delivery channel through
|
||
|
which the snapshot is delivered.
|
||
|
|
||
|
"""
|
||
|
params = {'deliveryChannelName': delivery_channel_name, }
|
||
|
return self.make_request(action='DeliverConfigSnapshot',
|
||
|
body=json.dumps(params))
|
||
|
|
||
|
def describe_configuration_recorder_status(self,
|
||
|
configuration_recorder_names=None):
|
||
|
"""
|
||
|
Returns the current status of the specified configuration
|
||
|
recorder. If a configuration recorder is not specified, this
|
||
|
action returns the status of all configuration recorder
|
||
|
associated with the account.
|
||
|
|
||
|
:type configuration_recorder_names: list
|
||
|
:param configuration_recorder_names: The name(s) of the configuration
|
||
|
recorder. If the name is not specified, the action returns the
|
||
|
current status of all the configuration recorders associated with
|
||
|
the account.
|
||
|
|
||
|
"""
|
||
|
params = {}
|
||
|
if configuration_recorder_names is not None:
|
||
|
params['ConfigurationRecorderNames'] = configuration_recorder_names
|
||
|
return self.make_request(action='DescribeConfigurationRecorderStatus',
|
||
|
body=json.dumps(params))
|
||
|
|
||
|
def describe_configuration_recorders(self,
|
||
|
configuration_recorder_names=None):
|
||
|
"""
|
||
|
Returns the name of one or more specified configuration
|
||
|
recorders. If the recorder name is not specified, this action
|
||
|
returns the names of all the configuration recorders
|
||
|
associated with the account.
|
||
|
|
||
|
:type configuration_recorder_names: list
|
||
|
:param configuration_recorder_names: A list of configuration recorder
|
||
|
names.
|
||
|
|
||
|
"""
|
||
|
params = {}
|
||
|
if configuration_recorder_names is not None:
|
||
|
params['ConfigurationRecorderNames'] = configuration_recorder_names
|
||
|
return self.make_request(action='DescribeConfigurationRecorders',
|
||
|
body=json.dumps(params))
|
||
|
|
||
|
def describe_delivery_channel_status(self, delivery_channel_names=None):
|
||
|
"""
|
||
|
Returns the current status of the specified delivery channel.
|
||
|
If a delivery channel is not specified, this action returns
|
||
|
the current status of all delivery channels associated with
|
||
|
the account.
|
||
|
|
||
|
:type delivery_channel_names: list
|
||
|
:param delivery_channel_names: A list of delivery channel names.
|
||
|
|
||
|
"""
|
||
|
params = {}
|
||
|
if delivery_channel_names is not None:
|
||
|
params['DeliveryChannelNames'] = delivery_channel_names
|
||
|
return self.make_request(action='DescribeDeliveryChannelStatus',
|
||
|
body=json.dumps(params))
|
||
|
|
||
|
def describe_delivery_channels(self, delivery_channel_names=None):
|
||
|
"""
|
||
|
Returns details about the specified delivery channel. If a
|
||
|
delivery channel is not specified, this action returns the
|
||
|
details of all delivery channels associated with the account.
|
||
|
|
||
|
:type delivery_channel_names: list
|
||
|
:param delivery_channel_names: A list of delivery channel names.
|
||
|
|
||
|
"""
|
||
|
params = {}
|
||
|
if delivery_channel_names is not None:
|
||
|
params['DeliveryChannelNames'] = delivery_channel_names
|
||
|
return self.make_request(action='DescribeDeliveryChannels',
|
||
|
body=json.dumps(params))
|
||
|
|
||
|
def get_resource_config_history(self, resource_type, resource_id,
|
||
|
later_time=None, earlier_time=None,
|
||
|
chronological_order=None, limit=None,
|
||
|
next_token=None):
|
||
|
"""
|
||
|
Returns a list of configuration items for the specified
|
||
|
resource. The list contains details about each state of the
|
||
|
resource during the specified time interval. You can specify a
|
||
|
`limit` on the number of results returned on the page. If a
|
||
|
limit is specified, a `nextToken` is returned as part of the
|
||
|
result that you can use to continue this request.
|
||
|
|
||
|
:type resource_type: string
|
||
|
:param resource_type: The resource type.
|
||
|
|
||
|
:type resource_id: string
|
||
|
:param resource_id: The ID of the resource (for example., `sg-xxxxxx`).
|
||
|
|
||
|
:type later_time: timestamp
|
||
|
:param later_time: The time stamp that indicates a later time. If not
|
||
|
specified, current time is taken.
|
||
|
|
||
|
:type earlier_time: timestamp
|
||
|
:param earlier_time: The time stamp that indicates an earlier time. If
|
||
|
not specified, the action returns paginated results that contain
|
||
|
configuration items that start from when the first configuration
|
||
|
item was recorded.
|
||
|
|
||
|
:type chronological_order: string
|
||
|
:param chronological_order: The chronological order for configuration
|
||
|
items listed. By default the results are listed in reverse
|
||
|
chronological order.
|
||
|
|
||
|
:type limit: integer
|
||
|
:param limit: The maximum number of configuration items returned in
|
||
|
each page. The default is 10. You cannot specify a limit greater
|
||
|
than 100.
|
||
|
|
||
|
:type next_token: string
|
||
|
:param next_token: An optional parameter used for pagination of the
|
||
|
results.
|
||
|
|
||
|
"""
|
||
|
params = {
|
||
|
'resourceType': resource_type,
|
||
|
'resourceId': resource_id,
|
||
|
}
|
||
|
if later_time is not None:
|
||
|
params['laterTime'] = later_time
|
||
|
if earlier_time is not None:
|
||
|
params['earlierTime'] = earlier_time
|
||
|
if chronological_order is not None:
|
||
|
params['chronologicalOrder'] = chronological_order
|
||
|
if limit is not None:
|
||
|
params['limit'] = limit
|
||
|
if next_token is not None:
|
||
|
params['nextToken'] = next_token
|
||
|
return self.make_request(action='GetResourceConfigHistory',
|
||
|
body=json.dumps(params))
|
||
|
|
||
|
def put_configuration_recorder(self, configuration_recorder):
|
||
|
"""
|
||
|
Creates a new configuration recorder to record the resource
|
||
|
configurations.
|
||
|
|
||
|
You can use this action to change the role ( `roleARN`) of an
|
||
|
existing recorder. To change the role, call the action on the
|
||
|
existing configuration recorder and specify a role.
|
||
|
|
||
|
:type configuration_recorder: dict
|
||
|
:param configuration_recorder: The configuration recorder object that
|
||
|
records each configuration change made to the resources. The
|
||
|
format should follow:
|
||
|
|
||
|
{'name': 'myrecorder',
|
||
|
'roleARN': 'arn:aws:iam::123456789012:role/trusted-aws-config'}
|
||
|
|
||
|
"""
|
||
|
params = {'ConfigurationRecorder': configuration_recorder, }
|
||
|
return self.make_request(action='PutConfigurationRecorder',
|
||
|
body=json.dumps(params))
|
||
|
|
||
|
def put_delivery_channel(self, delivery_channel):
|
||
|
"""
|
||
|
Creates a new delivery channel object to deliver the
|
||
|
configuration information to an Amazon S3 bucket, and to an
|
||
|
Amazon SNS topic.
|
||
|
|
||
|
You can use this action to change the Amazon S3 bucket or an
|
||
|
Amazon SNS topic of the existing delivery channel. To change
|
||
|
the Amazon S3 bucket or an Amazon SNS topic, call this action
|
||
|
and specify the changed values for the S3 bucket and the SNS
|
||
|
topic. If you specify a different value for either the S3
|
||
|
bucket or the SNS topic, this action will keep the existing
|
||
|
value for the parameter that is not changed.
|
||
|
|
||
|
:type delivery_channel: dict
|
||
|
:param delivery_channel: The configuration delivery channel object that
|
||
|
delivers the configuration information to an Amazon S3 bucket, and
|
||
|
to an Amazon SNS topic.
|
||
|
|
||
|
"""
|
||
|
params = {'DeliveryChannel': delivery_channel, }
|
||
|
return self.make_request(action='PutDeliveryChannel',
|
||
|
body=json.dumps(params))
|
||
|
|
||
|
def start_configuration_recorder(self, configuration_recorder_name):
|
||
|
"""
|
||
|
Starts recording configurations of all the resources
|
||
|
associated with the account.
|
||
|
|
||
|
You must have created at least one delivery channel to
|
||
|
successfully start the configuration recorder.
|
||
|
|
||
|
:type configuration_recorder_name: string
|
||
|
:param configuration_recorder_name: The name of the recorder object
|
||
|
that records each configuration change made to the resources.
|
||
|
|
||
|
"""
|
||
|
params = {
|
||
|
'ConfigurationRecorderName': configuration_recorder_name,
|
||
|
}
|
||
|
return self.make_request(action='StartConfigurationRecorder',
|
||
|
body=json.dumps(params))
|
||
|
|
||
|
def stop_configuration_recorder(self, configuration_recorder_name):
|
||
|
"""
|
||
|
Stops recording configurations of all the resources associated
|
||
|
with the account.
|
||
|
|
||
|
:type configuration_recorder_name: string
|
||
|
:param configuration_recorder_name: The name of the recorder object
|
||
|
that records each configuration change made to the resources.
|
||
|
|
||
|
"""
|
||
|
params = {
|
||
|
'ConfigurationRecorderName': configuration_recorder_name,
|
||
|
}
|
||
|
return self.make_request(action='StopConfigurationRecorder',
|
||
|
body=json.dumps(params))
|
||
|
|
||
|
def make_request(self, action, body):
|
||
|
headers = {
|
||
|
'X-Amz-Target': '%s.%s' % (self.TargetPrefix, action),
|
||
|
'Host': self.region.endpoint,
|
||
|
'Content-Type': 'application/x-amz-json-1.1',
|
||
|
'Content-Length': str(len(body)),
|
||
|
}
|
||
|
http_request = self.build_base_http_request(
|
||
|
method='POST', path='/', auth_path='/', params={},
|
||
|
headers=headers, data=body)
|
||
|
response = self._mexe(http_request, sender=None,
|
||
|
override_num_retries=10)
|
||
|
response_body = response.read().decode('utf-8')
|
||
|
boto.log.debug(response_body)
|
||
|
if response.status == 200:
|
||
|
if response_body:
|
||
|
return json.loads(response_body)
|
||
|
else:
|
||
|
json_body = json.loads(response_body)
|
||
|
fault_name = json_body.get('__type', None)
|
||
|
exception_class = self._faults.get(fault_name, self.ResponseError)
|
||
|
raise exception_class(response.status, response.reason,
|
||
|
body=json_body)
|
||
|
|