Skip to content

Commit 0b5da92

Browse files
committed
AWS Config for Python
1 parent 4f17481 commit 0b5da92

File tree

7 files changed

+871
-0
lines changed

7 files changed

+871
-0
lines changed

python/example_code/config/README.md

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,28 @@ Code excerpts that show you how to call individual service functions.
4040

4141
- [DeleteConfigRule](config_rules.py#L89)
4242
- [DescribeConfigRules](config_rules.py#L67)
43+
- [DescribeConfigurationRecorders](config_rules.py#L150)
44+
- [DescribeConfigurationRecorderStatus](config_rules.py#L175)
45+
- [GetResourceConfigHistory](config_rules.py#L225)
46+
- [ListDiscoveredResources](config_rules.py#L200)
4347
- [PutConfigRule](config_rules.py#L34)
48+
- [PutConfigurationRecorder](config_rules.py#L105)
49+
- [PutDeliveryChannel](config_rules.py#L130)
50+
- [StartConfigurationRecorder](config_rules.py#L155)
51+
- [StopConfigurationRecorder](config_rules.py#L250)
52+
53+
### Hello
54+
55+
Code example that shows you how to get started using the service.
56+
57+
- [Hello AWS Config](config_hello.py)
58+
59+
### Scenarios
60+
61+
Code examples that show you how to accomplish a specific task by calling multiple
62+
functions within the same service.
63+
64+
- [Get started with AWS Config](config_basics.py)
4465

4566

4667
<!--custom.examples.start-->
Lines changed: 356 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,356 @@
1+
# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2+
# SPDX-License-Identifier: Apache-2.0
3+
4+
"""
5+
Purpose
6+
7+
Shows how to use the AWS SDK for Python (Boto3) to work with AWS Config.
8+
This scenario demonstrates how to:
9+
- Set up a configuration recorder to track AWS resource configurations
10+
- Create a delivery channel to specify where Config sends configuration snapshots
11+
- Start the configuration recorder to begin monitoring resources
12+
- Monitor configuration recorder status and settings
13+
- Discover AWS resources in the account
14+
- Retrieve configuration history for specific resources
15+
- Clean up resources when done
16+
17+
This example requires an S3 bucket and IAM role with appropriate permissions.
18+
"""
19+
20+
import logging
21+
import time
22+
import boto3
23+
from botocore.exceptions import ClientError
24+
25+
from config_rules import ConfigWrapper
26+
import sys
27+
import os
28+
sys.path.append(os.path.join(os.path.dirname(__file__), '..', '..'))
29+
import demo_tools.question as q
30+
31+
logger = logging.getLogger(__name__)
32+
33+
34+
# snippet-start:[python.example_code.config-service.Scenario_ConfigBasics]
35+
class ConfigBasicsScenario:
36+
"""
37+
Runs an interactive scenario that shows how to get started with AWS Config.
38+
"""
39+
40+
def __init__(self, config_wrapper, s3_resource, iam_resource):
41+
"""
42+
:param config_wrapper: An object that wraps AWS Config operations.
43+
:param s3_resource: A Boto3 S3 resource.
44+
:param iam_resource: A Boto3 IAM resource.
45+
"""
46+
self.config_wrapper = config_wrapper
47+
self.s3_resource = s3_resource
48+
self.iam_resource = iam_resource
49+
self.recorder_name = None
50+
self.channel_name = None
51+
self.bucket_name = None
52+
self.role_arn = None
53+
54+
def run_scenario(self):
55+
"""
56+
Runs the scenario.
57+
"""
58+
print("-" * 88)
59+
print("Welcome to the AWS Config basics scenario!")
60+
print("-" * 88)
61+
62+
print(
63+
"AWS Config provides a detailed view of the resources associated with your AWS account, "
64+
"including how they are configured, how they are related to one another, and how the "
65+
"configurations and their relationships have changed over time."
66+
)
67+
print()
68+
69+
# Setup phase
70+
if not self._setup_resources():
71+
return
72+
73+
try:
74+
# Configuration monitoring phase
75+
self._demonstrate_configuration_monitoring()
76+
77+
# Resource discovery phase
78+
self._demonstrate_resource_discovery()
79+
80+
# Configuration history phase
81+
self._demonstrate_configuration_history()
82+
83+
finally:
84+
# Cleanup phase
85+
self._cleanup_resources()
86+
87+
print("Thanks for watching!")
88+
print("-" * 88)
89+
90+
def _setup_resources(self):
91+
"""
92+
Sets up the necessary resources for the scenario.
93+
"""
94+
print("\n" + "-" * 60)
95+
print("Setup")
96+
print("-" * 60)
97+
98+
# Get S3 bucket for delivery channel
99+
self.bucket_name = q.ask(
100+
"Enter the name of an S3 bucket for Config to deliver configuration snapshots "
101+
"(the bucket must exist and have appropriate permissions): ",
102+
q.non_empty
103+
)
104+
105+
# Verify bucket exists
106+
try:
107+
self.s3_resource.meta.client.head_bucket(Bucket=self.bucket_name)
108+
print(f"✓ S3 bucket '{self.bucket_name}' found.")
109+
except ClientError as err:
110+
if err.response['Error']['Code'] == '404':
111+
print(f"✗ S3 bucket '{self.bucket_name}' not found.")
112+
return False
113+
else:
114+
print(f"✗ Error accessing S3 bucket: {err}")
115+
return False
116+
117+
# Get IAM role ARN
118+
self.role_arn = q.ask(
119+
"Enter the ARN of an IAM role that grants AWS Config permissions to access your resources "
120+
"(e.g., arn:aws:iam::123456789012:role/config-role): ",
121+
q.non_empty
122+
)
123+
124+
# Verify role exists
125+
try:
126+
role_name = self.role_arn.split('/')[-1]
127+
self.iam_resource.Role(role_name).load()
128+
print(f"✓ IAM role found.")
129+
except ClientError as err:
130+
print(f"✗ Error accessing IAM role: {err}")
131+
return False
132+
133+
# Create configuration recorder
134+
self.recorder_name = "demo-config-recorder"
135+
print(f"\nCreating configuration recorder '{self.recorder_name}'...")
136+
try:
137+
self.config_wrapper.put_configuration_recorder(
138+
self.recorder_name,
139+
self.role_arn
140+
)
141+
print("✓ Configuration recorder created successfully.")
142+
except ClientError as err:
143+
if 'MaxNumberOfConfigurationRecordersExceededException' in str(err):
144+
print("✗ Maximum number of configuration recorders exceeded.")
145+
print("You can have only one configuration recorder per region.")
146+
return False
147+
else:
148+
print(f"✗ Error creating configuration recorder: {err}")
149+
return False
150+
151+
# Create delivery channel
152+
self.channel_name = "demo-delivery-channel"
153+
print(f"\nCreating delivery channel '{self.channel_name}'...")
154+
try:
155+
self.config_wrapper.put_delivery_channel(
156+
self.channel_name,
157+
self.bucket_name,
158+
"config-snapshots/"
159+
)
160+
print("✓ Delivery channel created successfully.")
161+
except ClientError as err:
162+
print(f"✗ Error creating delivery channel: {err}")
163+
return False
164+
165+
# Start configuration recorder
166+
print(f"\nStarting configuration recorder '{self.recorder_name}'...")
167+
try:
168+
self.config_wrapper.start_configuration_recorder(self.recorder_name)
169+
print("✓ Configuration recorder started successfully.")
170+
print("AWS Config is now monitoring your resources!")
171+
except ClientError as err:
172+
print(f"✗ Error starting configuration recorder: {err}")
173+
return False
174+
175+
return True
176+
177+
def _demonstrate_configuration_monitoring(self):
178+
"""
179+
Demonstrates configuration monitoring capabilities.
180+
"""
181+
print("\n" + "-" * 60)
182+
print("Configuration Monitoring")
183+
print("-" * 60)
184+
185+
# Show recorder status
186+
print("Checking configuration recorder status...")
187+
try:
188+
statuses = self.config_wrapper.describe_configuration_recorder_status([self.recorder_name])
189+
if statuses:
190+
status = statuses[0]
191+
print(f"Recorder: {status['name']}")
192+
print(f"Recording: {status['recording']}")
193+
print(f"Last Status: {status.get('lastStatus', 'N/A')}")
194+
if 'lastStartTime' in status:
195+
print(f"Last Started: {status['lastStartTime']}")
196+
except ClientError as err:
197+
print(f"Error getting recorder status: {err}")
198+
199+
# Show recorder configuration
200+
print("\nConfiguration recorder settings:")
201+
try:
202+
recorders = self.config_wrapper.describe_configuration_recorders([self.recorder_name])
203+
if recorders:
204+
recorder = recorders[0]
205+
recording_group = recorder.get('recordingGroup', {})
206+
print(f"Recording all supported resources: {recording_group.get('allSupported', False)}")
207+
print(f"Including global resources: {recording_group.get('includeGlobalResourceTypes', False)}")
208+
209+
if not recording_group.get('allSupported', True):
210+
resource_types = recording_group.get('resourceTypes', [])
211+
print(f"Specific resource types: {', '.join(resource_types)}")
212+
except ClientError as err:
213+
print(f"Error getting recorder configuration: {err}")
214+
215+
# Wait a moment for resources to be discovered
216+
print("\nWaiting for AWS Config to discover resources...")
217+
time.sleep(10)
218+
219+
def _demonstrate_resource_discovery(self):
220+
"""
221+
Demonstrates resource discovery capabilities.
222+
"""
223+
print("\n" + "-" * 60)
224+
print("Resource Discovery")
225+
print("-" * 60)
226+
227+
# Common resource types to check
228+
resource_types = [
229+
'AWS::S3::Bucket',
230+
'AWS::EC2::Instance',
231+
'AWS::IAM::Role',
232+
'AWS::Lambda::Function'
233+
]
234+
235+
print("Discovering AWS resources in your account...")
236+
total_resources = 0
237+
238+
for resource_type in resource_types:
239+
try:
240+
resources = self.config_wrapper.list_discovered_resources(resource_type, limit=10)
241+
count = len(resources)
242+
total_resources += count
243+
print(f"{resource_type}: {count} resources")
244+
245+
# Show details for first few resources
246+
if resources and count > 0:
247+
print(f" Sample resources:")
248+
for i, resource in enumerate(resources[:3]):
249+
print(f" {i+1}. {resource.get('resourceId', 'N/A')} ({resource.get('resourceName', 'Unnamed')})")
250+
if count > 3:
251+
print(f" ... and {count - 3} more")
252+
print()
253+
254+
except ClientError as err:
255+
print(f"Error listing {resource_type}: {err}")
256+
257+
print(f"Total resources discovered: {total_resources}")
258+
259+
def _demonstrate_configuration_history(self):
260+
"""
261+
Demonstrates configuration history capabilities.
262+
"""
263+
print("\n" + "-" * 60)
264+
print("Configuration History")
265+
print("-" * 60)
266+
267+
# Try to get configuration history for the S3 bucket we're using
268+
print(f"Getting configuration history for S3 bucket '{self.bucket_name}'...")
269+
try:
270+
config_items = self.config_wrapper.get_resource_config_history(
271+
'AWS::S3::Bucket',
272+
self.bucket_name,
273+
limit=5
274+
)
275+
276+
if config_items:
277+
print(f"Found {len(config_items)} configuration item(s):")
278+
for i, item in enumerate(config_items):
279+
print(f"\n Configuration {i+1}:")
280+
print(f" Configuration Item Capture Time: {item.get('configurationItemCaptureTime', 'N/A')}")
281+
print(f" Configuration State Id: {item.get('configurationStateId', 'N/A')}")
282+
print(f" Configuration Item Status: {item.get('configurationItemStatus', 'N/A')}")
283+
print(f" Resource Type: {item.get('resourceType', 'N/A')}")
284+
print(f" Resource Id: {item.get('resourceId', 'N/A')}")
285+
286+
# Show some configuration details
287+
config_data = item.get('configuration')
288+
if config_data and isinstance(config_data, dict):
289+
print(f" Sample configuration keys: {list(config_data.keys())[:5]}")
290+
else:
291+
print("No configuration history found yet. This is normal for newly monitored resources.")
292+
print("Configuration history will be available after resources are modified.")
293+
294+
except ClientError as err:
295+
if 'ResourceNotDiscoveredException' in str(err):
296+
print("Resource not yet discovered by AWS Config. This is normal for new setups.")
297+
else:
298+
print(f"Error getting configuration history: {err}")
299+
300+
def _cleanup_resources(self):
301+
"""
302+
Cleans up resources created during the scenario.
303+
"""
304+
print("\n" + "-" * 60)
305+
print("Cleanup")
306+
print("-" * 60)
307+
308+
if self.recorder_name:
309+
cleanup = q.ask(
310+
f"Do you want to stop and delete the configuration recorder '{self.recorder_name}'? "
311+
"This will stop monitoring your resources. (y/n): ",
312+
q.is_yesno
313+
)
314+
315+
if cleanup:
316+
# Stop the configuration recorder
317+
print(f"Stopping configuration recorder '{self.recorder_name}'...")
318+
try:
319+
self.config_wrapper.stop_configuration_recorder(self.recorder_name)
320+
print("✓ Configuration recorder stopped.")
321+
except ClientError as err:
322+
print(f"Error stopping configuration recorder: {err}")
323+
324+
# Note: In a real scenario, you might also want to delete the recorder and delivery channel
325+
# However, this example leaves them for the user to manage manually
326+
print("\nNote: The configuration recorder and delivery channel have been left in your account.")
327+
print("You can manage them through the AWS Console or delete them manually if needed.")
328+
else:
329+
print("Configuration recorder left running. You can manage it through the AWS Console.")
330+
331+
print("\nScenario completed!")
332+
333+
334+
# snippet-end:[python.example_code.config-service.Scenario_ConfigBasics]
335+
336+
337+
def main():
338+
"""
339+
Runs the Config basics scenario.
340+
"""
341+
logging.basicConfig(level=logging.INFO, format="%(levelname)s: %(message)s")
342+
343+
print("-" * 88)
344+
print("Welcome to the AWS Config basics scenario!")
345+
print("-" * 88)
346+
347+
config_wrapper = ConfigWrapper(boto3.client('config'))
348+
s3_resource = boto3.resource('s3')
349+
iam_resource = boto3.resource('iam')
350+
351+
scenario = ConfigBasicsScenario(config_wrapper, s3_resource, iam_resource)
352+
scenario.run_scenario()
353+
354+
355+
if __name__ == '__main__':
356+
main()

0 commit comments

Comments
 (0)