Skip to content

Commit 9ddd566

Browse files
committed
Updates to tests and specification.
1 parent fc7092d commit 9ddd566

File tree

9 files changed

+503
-57
lines changed

9 files changed

+503
-57
lines changed

python/example_code/controltower/controltower_wrapper.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -303,11 +303,10 @@ def list_landing_zones(self):
303303
# snippet-end:[python.example_code.controltower.ListLandingZones]
304304

305305
# snippet-start:[python.example_code.controltower.ListEnabledBaselines]
306-
def list_enabled_baselines(self, target_identifier):
306+
def list_enabled_baselines(self):
307307
"""
308-
Lists all enabled baselines for a specific target.
308+
Lists all enabled baselines.
309309
310-
:param target_identifier: The identifier of the target (e.g., OU ARN).
311310
:return: List of enabled baselines.
312311
:raises ClientError: If the listing operation fails.
313312
"""
@@ -387,15 +386,16 @@ def disable_baseline(self, enabled_baseline_identifier):
387386

388387
return response['operationIdentifier']
389388
except ClientError as err:
390-
if err.response["Error"]["Code"] == "ResourceNotFoundException":
391-
logger.error("Target not found.")
389+
if err.response["Error"]["Code"] == "ConflictException":
390+
print(f"Conflict disabling baseline: {err.response['Error']['Message']}. Skipping disable step." )
391+
return None
392392
else:
393393
logger.error(
394394
"Couldn't disable baseline. Here's why: %s: %s",
395395
err.response["Error"]["Code"],
396396
err.response["Error"]["Message"]
397397
)
398-
raise
398+
raise
399399
# snippet-end:[python.example_code.controltower.DisableBaseline]
400400

401401
# snippet-start:[python.example_code.controltower.ListEnabledControls]

python/example_code/controltower/scenario_controltower.py

Lines changed: 18 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@ def run_scenario(self):
8282
self.ou_id = sandbox_ou_id
8383

8484
# List and Enable Baseline.
85+
print("\nManaging Baselines:")
8586
control_tower_baseline = None
8687
identity_center_baseline = None
8788
baselines = self.controltower_wrapper.list_baselines()
@@ -93,9 +94,7 @@ def run_scenario(self):
9394

9495
if self.use_landing_zone:
9596
print("\nListing enabled baselines:")
96-
enabled_baselines = self.controltower_wrapper.list_enabled_baselines(
97-
self.ou_arn
98-
)
97+
enabled_baselines = self.controltower_wrapper.list_enabled_baselines()
9998
for baseline in enabled_baselines:
10099
# If the Identity Center baseline is enabled, the identifier must be used for other baselines.
101100
if 'baseline/LN25R72TTG6IGPTQ' in baseline['baselineIdentifier']:
@@ -107,25 +106,27 @@ def run_scenario(self):
107106
q.is_yesno,
108107
):
109108
print("\nEnabling Control Tower Baseline.")
109+
ic_baseline_arn = identity_center_baseline['arn'] if identity_center_baseline else None
110110
baseline_arn = self.controltower_wrapper.enable_baseline(
111111
self.ou_arn,
112-
identity_center_baseline['arn'],
112+
ic_baseline_arn,
113113
control_tower_baseline['arn'],
114114
'4.0'
115115
)
116116
if baseline_arn:
117117
print(f"Enabled baseline ARN: {baseline_arn}")
118118
else:
119+
# Find the enabled baseline so we can reset it.
119120
for enabled_baseline in enabled_baselines:
120-
if enabled_baseline['arn'] == control_tower_baseline['arn']:
121-
control_tower_baseline = baseline
121+
if enabled_baseline['baselineIdentifier'] == control_tower_baseline['arn']:
122+
baseline_arn = enabled_baseline['arn']
122123
print("No change, the selected baseline was already enabled.")
123124

124125
if q.ask(
125126
f"Do you want to reset the Control Tower Baseline? (y/n) ",
126127
q.is_yesno,
127128
):
128-
print("\nResetting Control Tower Baseline.")
129+
print(f"\nResetting Control Tower Baseline. {baseline_arn}")
129130
operation_id = self.controltower_wrapper.reset_enabled_baseline(
130131
baseline_arn
131132
)
@@ -142,23 +143,22 @@ def run_scenario(self):
142143
print(f"\nDisabled baseline operation id {operation_id}.")
143144

144145
# List and Enable Controls.
145-
print("Managing Controls:")
146+
print("\nManaging Controls:")
146147
controls = self.controltower_wrapper.list_controls()
147148
print("\nListing first 5 available Controls:")
148149
for i, control in enumerate(controls[:5], 1):
149-
print(f"{i}. {control['Name']}")
150+
print(f"{i}. {control['Name']} - {control['Arn']}")
150151

151152
if self.use_landing_zone:
152-
153-
enabled_controls = self.controltower_wrapper.list_enabled_controls()
153+
target_ou = self.ou_arn
154+
enabled_controls = self.controltower_wrapper.list_enabled_controls(target_ou)
154155
print("\nListing enabled controls:")
155-
for i, control in enabled_controls:
156-
print(f"{i}. {control['Name']}")
156+
for i, control in enumerate(enabled_controls, 1):
157+
print(f"{i}. {control['controlIdentifier']}")
157158

158159
# Enable first non-enabled control as an example.
159-
enabled_control_arns = [control['Arn'] for control in enabled_controls]
160+
enabled_control_arns = [control['arn'] for control in enabled_controls]
160161
control_arn = next(control['Arn'] for control in controls if control['Arn'] not in enabled_control_arns)
161-
target_ou = self.ou_arn
162162

163163
if control_arn and q.ask(
164164
f"Do you want to enable the control {control_arn}? (y/n) ",
@@ -182,10 +182,10 @@ def run_scenario(self):
182182
control_arn, target_ou)
183183
print(f"Disable operation ID: {operation_id}")
184184

185-
print("This concludes the control tower scenario.")
185+
print("\nThis concludes the example scenario.")
186186

187-
print("Thanks for watching!")
188-
print("-" * 88)
187+
print("Thanks for watching!")
188+
print("-" * 88)
189189

190190
def setup_organization(self):
191191
"""
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2+
# SPDX-License-Identifier: Apache-2.0
3+
4+
"""
5+
Unit tests for the disable_baseline method in controltower_wrapper.py.
6+
"""
7+
8+
import pytest
9+
from botocore.exceptions import ClientError
10+
11+
class MockManager:
12+
def __init__(self, stub_runner, scenario_data, input_mocker):
13+
self.scenario_data = scenario_data
14+
self.ou_arn = "arn:aws:organizations::123456789012:ou/o-exampleorgid/ou-exampleouid"
15+
self.baseline_arn = "arn:aws:controltower:us-east-1:123456789012:baseline/AWSControlTowerBaseline/enabled"
16+
self.operation_id = "op-1234567890abcdef0"
17+
18+
self.stub_runner = stub_runner
19+
20+
def setup_stubs(self, error, stop_on, controltower_stubber):
21+
with self.stub_runner(error, stop_on) as runner:
22+
runner.add(
23+
controltower_stubber.stub_disable_baseline,
24+
self.ou_arn,
25+
self.baseline_arn,
26+
self.operation_id
27+
)
28+
29+
30+
@pytest.fixture
31+
def mock_mgr(stub_runner, scenario_data, input_mocker):
32+
return MockManager(stub_runner, scenario_data, input_mocker)
33+
34+
@pytest.mark.integ
35+
def test_disable_baseline(mock_mgr, capsys):
36+
mock_mgr.setup_stubs(None, None, mock_mgr.scenario_data.controltower_stubber)
37+
38+
# Test disabling a baseline
39+
operation_id = mock_mgr.scenario_data.scenario.controltower_wrapper.disable_baseline(
40+
mock_mgr.ou_arn,
41+
mock_mgr.baseline_arn
42+
)
43+
44+
# Verify the results
45+
assert operation_id == mock_mgr.operation_id
46+
47+
48+
@pytest.mark.parametrize(
49+
"error, stop_on_index",
50+
[
51+
("TESTERROR-stub_disable_baseline", 0),
52+
],
53+
)
54+
@pytest.mark.integ
55+
def test_disable_baseline_error(mock_mgr, caplog, error, stop_on_index):
56+
mock_mgr.setup_stubs(error, stop_on_index, mock_mgr.scenario_data.controltower_stubber)
57+
58+
with pytest.raises(ClientError) as exc_info:
59+
mock_mgr.scenario_data.scenario.controltower_wrapper.disable_baseline(
60+
mock_mgr.ou_arn,
61+
mock_mgr.baseline_arn
62+
)
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2+
# SPDX-License-Identifier: Apache-2.0
3+
4+
"""
5+
Unit tests for the list_enabled_baselines method in controltower_wrapper.py.
6+
"""
7+
8+
import pytest
9+
from botocore.exceptions import ClientError
10+
11+
class MockManager:
12+
def __init__(self, stub_runner, scenario_data, input_mocker):
13+
self.scenario_data = scenario_data
14+
self.ou_arn = "arn:aws:organizations::123456789012:ou/o-exampleorgid/ou-exampleouid"
15+
16+
self.enabled_baselines = [
17+
{
18+
"baselineArn": "arn:aws:controltower:us-east-1:123456789012:baseline/AWSControlTowerBaseline/enabled",
19+
"baselineVersion": "4.0",
20+
"baselineName": "AWSControlTowerBaseline"
21+
},
22+
{
23+
"baselineArn": "arn:aws:controltower:us-east-1:123456789012:baseline/OtherBaseline/enabled",
24+
"baselineVersion": "2.0",
25+
"baselineName": "OtherBaseline"
26+
}
27+
]
28+
29+
self.stub_runner = stub_runner
30+
31+
def setup_stubs(self, error, stop_on, controltower_stubber):
32+
with self.stub_runner(error, stop_on) as runner:
33+
runner.add(
34+
controltower_stubber.stub_list_enabled_baselines,
35+
self.ou_arn,
36+
self.enabled_baselines
37+
)
38+
39+
40+
@pytest.fixture
41+
def mock_mgr(stub_runner, scenario_data, input_mocker):
42+
return MockManager(stub_runner, scenario_data, input_mocker)
43+
44+
@pytest.mark.integ
45+
def test_list_enabled_baselines(mock_mgr, capsys):
46+
mock_mgr.setup_stubs(None, None, mock_mgr.scenario_data.controltower_stubber)
47+
48+
# Test listing enabled baselines
49+
enabled_baselines = mock_mgr.scenario_data.scenario.controltower_wrapper.list_enabled_baselines(
50+
mock_mgr.ou_arn
51+
)
52+
53+
# Verify the results
54+
assert len(enabled_baselines) == 2
55+
assert enabled_baselines[0]["baselineName"] == "AWSControlTowerBaseline"
56+
assert enabled_baselines[0]["baselineVersion"] == "4.0"
57+
assert enabled_baselines[1]["baselineName"] == "OtherBaseline"
58+
59+
60+
@pytest.mark.parametrize(
61+
"error, stop_on_index",
62+
[
63+
("TESTERROR-stub_list_enabled_baselines", 0),
64+
],
65+
)
66+
@pytest.mark.integ
67+
def test_list_enabled_baselines_error(mock_mgr, caplog, error, stop_on_index):
68+
mock_mgr.setup_stubs(error, stop_on_index, mock_mgr.scenario_data.controltower_stubber)
69+
70+
with pytest.raises(ClientError) as exc_info:
71+
mock_mgr.scenario_data.scenario.controltower_wrapper.list_enabled_baselines(
72+
mock_mgr.ou_arn
73+
)
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2+
# SPDX-License-Identifier: Apache-2.0
3+
4+
"""
5+
Unit tests for the list_enabled_controls method in controltower_wrapper.py.
6+
"""
7+
8+
import pytest
9+
from botocore.exceptions import ClientError
10+
11+
class MockManager:
12+
def __init__(self, stub_runner, scenario_data, input_mocker):
13+
self.scenario_data = scenario_data
14+
self.ou_arn = "arn:aws:organizations::123456789012:ou/o-exampleorgid/ou-exampleouid"
15+
16+
self.enabled_controls = [
17+
{
18+
"controlIdentifier": "arn:aws:controlcatalog:us-east-1:123456789012:control/aws-control-1234",
19+
"controlName": "TestControl1",
20+
"controlStatus": "ENABLED"
21+
},
22+
{
23+
"controlIdentifier": "arn:aws:controlcatalog:us-east-1:123456789012:control/aws-control-5678",
24+
"controlName": "TestControl2",
25+
"controlStatus": "ENABLED"
26+
}
27+
]
28+
29+
self.stub_runner = stub_runner
30+
31+
def setup_stubs(self, error, stop_on, controltower_stubber):
32+
with self.stub_runner(error, stop_on) as runner:
33+
runner.add(
34+
controltower_stubber.stub_list_enabled_controls,
35+
self.ou_arn,
36+
self.enabled_controls
37+
)
38+
39+
40+
@pytest.fixture
41+
def mock_mgr(stub_runner, scenario_data, input_mocker):
42+
return MockManager(stub_runner, scenario_data, input_mocker)
43+
44+
@pytest.mark.integ
45+
def test_list_enabled_controls(mock_mgr, capsys):
46+
mock_mgr.setup_stubs(None, None, mock_mgr.scenario_data.controltower_stubber)
47+
48+
# Test listing enabled controls
49+
enabled_controls = mock_mgr.scenario_data.scenario.controltower_wrapper.list_enabled_controls(
50+
mock_mgr.ou_arn
51+
)
52+
53+
# Verify the results
54+
assert len(enabled_controls) == 2
55+
assert enabled_controls[0]["controlName"] == "TestControl1"
56+
assert enabled_controls[0]["controlStatus"] == "ENABLED"
57+
assert enabled_controls[1]["controlName"] == "TestControl2"
58+
59+
60+
@pytest.mark.parametrize(
61+
"error, stop_on_index",
62+
[
63+
("TESTERROR-stub_list_enabled_controls", 0),
64+
],
65+
)
66+
@pytest.mark.integ
67+
def test_list_enabled_controls_error(mock_mgr, caplog, error, stop_on_index):
68+
mock_mgr.setup_stubs(error, stop_on_index, mock_mgr.scenario_data.controltower_stubber)
69+
70+
with pytest.raises(ClientError) as exc_info:
71+
mock_mgr.scenario_data.scenario.controltower_wrapper.list_enabled_controls(
72+
mock_mgr.ou_arn
73+
)

0 commit comments

Comments
 (0)