1# Copyright (c) 2015 Amazon.com, Inc. or its affiliates.  All Rights Reserved
2#
3# Permission is hereby granted, free of charge, to any person obtaining a
4# copy of this software and associated documentation files (the
5# "Software"), to deal in the Software without restriction, including
6# without limitation the rights to use, copy, modify, merge, publish, dis-
7# tribute, sublicense, and/or sell copies of the Software, and to permit
8# persons to whom the Software is furnished to do so, subject to the fol-
9# lowing conditions:
10#
11# The above copyright notice and this permission notice shall be included
12# in all copies or substantial portions of the Software.
13#
14# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
15# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABIL-
16# ITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
17# SHALL THE AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
18# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
20# IN THE SOFTWARE.
21#
22
23import boto
24from boto.compat import json
25from boto.connection import AWSQueryConnection
26from boto.regioninfo import RegionInfo
27from boto.exception import JSONResponseError
28from boto.configservice import exceptions
29
30
31class ConfigServiceConnection(AWSQueryConnection):
32    """
33    AWS Config
34    AWS Config provides a way to keep track of the configurations of
35    all the AWS resources associated with your AWS account. You can
36    use AWS Config to get the current and historical configurations of
37    each AWS resource and also to get information about the
38    relationship between the resources. An AWS resource can be an
39    Amazon Compute Cloud (Amazon EC2) instance, an Elastic Block Store
40    (EBS) volume, an Elastic network Interface (ENI), or a security
41    group. For a complete list of resources currently supported by AWS
42    Config, see `Supported AWS Resources`_.
43
44    You can access and manage AWS Config through the AWS Management
45    Console, the AWS Command Line Interface (AWS CLI), the AWS Config
46    API, or the AWS SDKs for AWS Config
47
48    This reference guide contains documentation for the AWS Config API
49    and the AWS CLI commands that you can use to manage AWS Config.
50
51    The AWS Config API uses the Signature Version 4 protocol for
52    signing requests. For more information about how to sign a request
53    with this protocol, see `Signature Version 4 Signing Process`_.
54
55    For detailed information about AWS Config features and their
56    associated actions or commands, as well as how to work with AWS
57    Management Console, see `What Is AWS Config?`_ in the AWS Config
58    Developer Guide .
59    """
60    APIVersion = "2014-11-12"
61    DefaultRegionName = "us-east-1"
62    DefaultRegionEndpoint = "config.us-east-1.amazonaws.com"
63    ServiceName = "ConfigService"
64    TargetPrefix = "StarlingDoveService"
65    ResponseError = JSONResponseError
66
67    _faults = {
68        "InvalidLimitException": exceptions.InvalidLimitException,
69        "NoSuchBucketException": exceptions.NoSuchBucketException,
70        "InvalidSNSTopicARNException": exceptions.InvalidSNSTopicARNException,
71        "ResourceNotDiscoveredException": exceptions.ResourceNotDiscoveredException,
72        "MaxNumberOfDeliveryChannelsExceededException": exceptions.MaxNumberOfDeliveryChannelsExceededException,
73        "LastDeliveryChannelDeleteFailedException": exceptions.LastDeliveryChannelDeleteFailedException,
74        "InsufficientDeliveryPolicyException": exceptions.InsufficientDeliveryPolicyException,
75        "InvalidRoleException": exceptions.InvalidRoleException,
76        "InvalidTimeRangeException": exceptions.InvalidTimeRangeException,
77        "NoSuchDeliveryChannelException": exceptions.NoSuchDeliveryChannelException,
78        "NoSuchConfigurationRecorderException": exceptions.NoSuchConfigurationRecorderException,
79        "InvalidS3KeyPrefixException": exceptions.InvalidS3KeyPrefixException,
80        "InvalidDeliveryChannelNameException": exceptions.InvalidDeliveryChannelNameException,
81        "NoRunningConfigurationRecorderException": exceptions.NoRunningConfigurationRecorderException,
82        "ValidationException": exceptions.ValidationException,
83        "NoAvailableConfigurationRecorderException": exceptions.NoAvailableConfigurationRecorderException,
84        "InvalidNextTokenException": exceptions.InvalidNextTokenException,
85        "InvalidConfigurationRecorderNameException": exceptions.InvalidConfigurationRecorderNameException,
86        "NoAvailableDeliveryChannelException": exceptions.NoAvailableDeliveryChannelException,
87        "MaxNumberOfConfigurationRecordersExceededException": exceptions.MaxNumberOfConfigurationRecordersExceededException,
88    }
89
90
91    def __init__(self, **kwargs):
92        region = kwargs.pop('region', None)
93        if not region:
94            region = RegionInfo(self, self.DefaultRegionName,
95                                self.DefaultRegionEndpoint)
96
97        if 'host' not in kwargs or kwargs['host'] is None:
98            kwargs['host'] = region.endpoint
99
100        super(ConfigServiceConnection, self).__init__(**kwargs)
101        self.region = region
102
103    def _required_auth_capability(self):
104        return ['hmac-v4']
105
106    def delete_delivery_channel(self, delivery_channel_name):
107        """
108        Deletes the specified delivery channel.
109
110        The delivery channel cannot be deleted if it is the only
111        delivery channel and the configuration recorder is still
112        running. To delete the delivery channel, stop the running
113        configuration recorder using the StopConfigurationRecorder
114        action.
115
116        :type delivery_channel_name: string
117        :param delivery_channel_name: The name of the delivery channel to
118            delete.
119
120        """
121        params = {'DeliveryChannelName': delivery_channel_name, }
122        return self.make_request(action='DeleteDeliveryChannel',
123                                 body=json.dumps(params))
124
125    def deliver_config_snapshot(self, delivery_channel_name):
126        """
127        Schedules delivery of a configuration snapshot to the Amazon
128        S3 bucket in the specified delivery channel. After the
129        delivery has started, AWS Config sends following notifications
130        using an Amazon SNS topic that you have specified.
131
132
133        + Notification of starting the delivery.
134        + Notification of delivery completed, if the delivery was
135          successfully completed.
136        + Notification of delivery failure, if the delivery failed to
137          complete.
138
139        :type delivery_channel_name: string
140        :param delivery_channel_name: The name of the delivery channel through
141            which the snapshot is delivered.
142
143        """
144        params = {'deliveryChannelName': delivery_channel_name, }
145        return self.make_request(action='DeliverConfigSnapshot',
146                                 body=json.dumps(params))
147
148    def describe_configuration_recorder_status(self,
149                                               configuration_recorder_names=None):
150        """
151        Returns the current status of the specified configuration
152        recorder. If a configuration recorder is not specified, this
153        action returns the status of all configuration recorder
154        associated with the account.
155
156        :type configuration_recorder_names: list
157        :param configuration_recorder_names: The name(s) of the configuration
158            recorder. If the name is not specified, the action returns the
159            current status of all the configuration recorders associated with
160            the account.
161
162        """
163        params = {}
164        if configuration_recorder_names is not None:
165            params['ConfigurationRecorderNames'] = configuration_recorder_names
166        return self.make_request(action='DescribeConfigurationRecorderStatus',
167                                 body=json.dumps(params))
168
169    def describe_configuration_recorders(self,
170                                         configuration_recorder_names=None):
171        """
172        Returns the name of one or more specified configuration
173        recorders. If the recorder name is not specified, this action
174        returns the names of all the configuration recorders
175        associated with the account.
176
177        :type configuration_recorder_names: list
178        :param configuration_recorder_names: A list of configuration recorder
179            names.
180
181        """
182        params = {}
183        if configuration_recorder_names is not None:
184            params['ConfigurationRecorderNames'] = configuration_recorder_names
185        return self.make_request(action='DescribeConfigurationRecorders',
186                                 body=json.dumps(params))
187
188    def describe_delivery_channel_status(self, delivery_channel_names=None):
189        """
190        Returns the current status of the specified delivery channel.
191        If a delivery channel is not specified, this action returns
192        the current status of all delivery channels associated with
193        the account.
194
195        :type delivery_channel_names: list
196        :param delivery_channel_names: A list of delivery channel names.
197
198        """
199        params = {}
200        if delivery_channel_names is not None:
201            params['DeliveryChannelNames'] = delivery_channel_names
202        return self.make_request(action='DescribeDeliveryChannelStatus',
203                                 body=json.dumps(params))
204
205    def describe_delivery_channels(self, delivery_channel_names=None):
206        """
207        Returns details about the specified delivery channel. If a
208        delivery channel is not specified, this action returns the
209        details of all delivery channels associated with the account.
210
211        :type delivery_channel_names: list
212        :param delivery_channel_names: A list of delivery channel names.
213
214        """
215        params = {}
216        if delivery_channel_names is not None:
217            params['DeliveryChannelNames'] = delivery_channel_names
218        return self.make_request(action='DescribeDeliveryChannels',
219                                 body=json.dumps(params))
220
221    def get_resource_config_history(self, resource_type, resource_id,
222                                    later_time=None, earlier_time=None,
223                                    chronological_order=None, limit=None,
224                                    next_token=None):
225        """
226        Returns a list of configuration items for the specified
227        resource. The list contains details about each state of the
228        resource during the specified time interval. You can specify a
229        `limit` on the number of results returned on the page. If a
230        limit is specified, a `nextToken` is returned as part of the
231        result that you can use to continue this request.
232
233        :type resource_type: string
234        :param resource_type: The resource type.
235
236        :type resource_id: string
237        :param resource_id: The ID of the resource (for example., `sg-xxxxxx`).
238
239        :type later_time: timestamp
240        :param later_time: The time stamp that indicates a later time. If not
241            specified, current time is taken.
242
243        :type earlier_time: timestamp
244        :param earlier_time: The time stamp that indicates an earlier time. If
245            not specified, the action returns paginated results that contain
246            configuration items that start from when the first configuration
247            item was recorded.
248
249        :type chronological_order: string
250        :param chronological_order: The chronological order for configuration
251            items listed. By default the results are listed in reverse
252            chronological order.
253
254        :type limit: integer
255        :param limit: The maximum number of configuration items returned in
256            each page. The default is 10. You cannot specify a limit greater
257            than 100.
258
259        :type next_token: string
260        :param next_token: An optional parameter used for pagination of the
261            results.
262
263        """
264        params = {
265            'resourceType': resource_type,
266            'resourceId': resource_id,
267        }
268        if later_time is not None:
269            params['laterTime'] = later_time
270        if earlier_time is not None:
271            params['earlierTime'] = earlier_time
272        if chronological_order is not None:
273            params['chronologicalOrder'] = chronological_order
274        if limit is not None:
275            params['limit'] = limit
276        if next_token is not None:
277            params['nextToken'] = next_token
278        return self.make_request(action='GetResourceConfigHistory',
279                                 body=json.dumps(params))
280
281    def put_configuration_recorder(self, configuration_recorder):
282        """
283        Creates a new configuration recorder to record the resource
284        configurations.
285
286        You can use this action to change the role ( `roleARN`) of an
287        existing recorder. To change the role, call the action on the
288        existing configuration recorder and specify a role.
289
290        :type configuration_recorder: dict
291        :param configuration_recorder: The configuration recorder object that
292            records each configuration change made to the resources.
293
294        """
295        params = {'ConfigurationRecorder': configuration_recorder, }
296        return self.make_request(action='PutConfigurationRecorder',
297                                 body=json.dumps(params))
298
299    def put_delivery_channel(self, delivery_channel):
300        """
301        Creates a new delivery channel object to deliver the
302        configuration information to an Amazon S3 bucket, and to an
303        Amazon SNS topic.
304
305        You can use this action to change the Amazon S3 bucket or an
306        Amazon SNS topic of the existing delivery channel. To change
307        the Amazon S3 bucket or an Amazon SNS topic, call this action
308        and specify the changed values for the S3 bucket and the SNS
309        topic. If you specify a different value for either the S3
310        bucket or the SNS topic, this action will keep the existing
311        value for the parameter that is not changed.
312
313        :type delivery_channel: dict
314        :param delivery_channel: The configuration delivery channel object that
315            delivers the configuration information to an Amazon S3 bucket, and
316            to an Amazon SNS topic.
317
318        """
319        params = {'DeliveryChannel': delivery_channel, }
320        return self.make_request(action='PutDeliveryChannel',
321                                 body=json.dumps(params))
322
323    def start_configuration_recorder(self, configuration_recorder_name):
324        """
325        Starts recording configurations of all the resources
326        associated with the account.
327
328        You must have created at least one delivery channel to
329        successfully start the configuration recorder.
330
331        :type configuration_recorder_name: string
332        :param configuration_recorder_name: The name of the recorder object
333            that records each configuration change made to the resources.
334
335        """
336        params = {
337            'ConfigurationRecorderName': configuration_recorder_name,
338        }
339        return self.make_request(action='StartConfigurationRecorder',
340                                 body=json.dumps(params))
341
342    def stop_configuration_recorder(self, configuration_recorder_name):
343        """
344        Stops recording configurations of all the resources associated
345        with the account.
346
347        :type configuration_recorder_name: string
348        :param configuration_recorder_name: The name of the recorder object
349            that records each configuration change made to the resources.
350
351        """
352        params = {
353            'ConfigurationRecorderName': configuration_recorder_name,
354        }
355        return self.make_request(action='StopConfigurationRecorder',
356                                 body=json.dumps(params))
357
358    def make_request(self, action, body):
359        headers = {
360            'X-Amz-Target': '%s.%s' % (self.TargetPrefix, action),
361            'Host': self.region.endpoint,
362            'Content-Type': 'application/x-amz-json-1.1',
363            'Content-Length': str(len(body)),
364        }
365        http_request = self.build_base_http_request(
366            method='POST', path='/', auth_path='/', params={},
367            headers=headers, data=body)
368        response = self._mexe(http_request, sender=None,
369                              override_num_retries=10)
370        response_body = response.read().decode('utf-8')
371        boto.log.debug(response_body)
372        if response.status == 200:
373            if response_body:
374                return json.loads(response_body)
375        else:
376            json_body = json.loads(response_body)
377            fault_name = json_body.get('__type', None)
378            exception_class = self._faults.get(fault_name, self.ResponseError)
379            raise exception_class(response.status, response.reason,
380                                  body=json_body)
381
382