-
Notifications
You must be signed in to change notification settings - Fork 226
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Feature: Account Management Statemachine will now opt-in to target re…
…gions when creating an account (#604) * Initial commit for enabling regions via the API * Code Review Changes * Update src/template.yml Co-authored-by: Simon Kok * Fix line length - fixes lint issue --------- Co-authored-by: Stewart Wallace Co-authored-by: Simon Kok Co-authored-by: Javy de Koning
- Loading branch information
Showing
3 changed files
with
309 additions
and
2 deletions.
There are no files selected for viewing
103 changes: 103 additions & 0 deletions
103
src/lambda_codebase/account_processing/configure_account_regions.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,103 @@ | ||
# Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. | ||
# SPDX-License-Identifier: MIT-0 | ||
|
||
""" | ||
Takes regions that the account is not-opted into and opts into them. | ||
""" | ||
from ast import literal_eval | ||
|
||
|
||
import boto3 | ||
from aws_xray_sdk.core import patch_all | ||
from logger import configure_logger | ||
|
||
patch_all() | ||
LOGGER = configure_logger(__name__) | ||
|
||
|
||
def get_regions_from_ssm(ssm_client): | ||
regions = ssm_client.get_parameter(Name="target_regions")["Parameter"].get("Value") | ||
regions = literal_eval(regions) | ||
return regions | ||
|
||
|
||
def get_region_status(account_client, **list_region_args): | ||
region_status_response = account_client.list_regions(**list_region_args) | ||
region_status = { | ||
region.get("RegionName"): region.get("RegionOptStatus") | ||
for region in region_status_response.get("Regions") | ||
} | ||
# Currently no built in paginator for list_regions... | ||
# So we have to do this manually. | ||
next_token = region_status_response.get("NextToken") | ||
if next_token: | ||
while next_token: | ||
list_region_args["NextToken"] = next_token | ||
region_status_response = account_client.list_regions(**list_region_args) | ||
next_token = region_status_response.get("NextToken") | ||
region_status = region_status | { | ||
region.get("RegionName"): region.get("RegionOptStatus") | ||
for region in region_status_response.get("Regions") | ||
} | ||
return region_status | ||
|
||
|
||
def enable_regions_for_account( | ||
account_client, account_id, desired_regions, org_root_account_id | ||
): | ||
list_region_args = {} | ||
enable_region_args = {} | ||
target_is_different_account = org_root_account_id != account_id | ||
if target_is_different_account: | ||
list_region_args["AccountId"] = account_id | ||
enable_region_args["AccountId"] = account_id | ||
|
||
region_status = get_region_status(account_client, **list_region_args) | ||
|
||
regions_enabled = {} | ||
for region in desired_regions: | ||
regions_enabled[region] = False | ||
desired_region_status = region_status.get(region.lower()) | ||
if not desired_region_status: | ||
LOGGER.warning("Unable to obtain status of %s, not enabling") | ||
if desired_region_status == "DISABLED": | ||
LOGGER.info("Enabling Region %s because it is currently Disabled", region) | ||
enable_region_args["RegionName"] = region.lower() | ||
account_client.enable_region(**enable_region_args) | ||
else: | ||
LOGGER.info( | ||
"Not enabling Region: %s because it is: %s", | ||
region, | ||
desired_region_status, | ||
) | ||
if desired_region_status in ["ENABLED_BY_DEFAULT", "ENABLED"]: | ||
regions_enabled[region] = True | ||
LOGGER.info(regions_enabled) | ||
return all(regions_enabled.values()) | ||
|
||
|
||
def lambda_handler(event, _): | ||
desired_regions = [] | ||
if event.get("regions"): | ||
LOGGER.info( | ||
"Account Level Regions is not currently supported." | ||
"Ignoring these values for now and using SSM only" | ||
) | ||
desired_regions.extend(get_regions_from_ssm(boto3.client("ssm"))) | ||
org_root_account_id = boto3.client("sts").get_caller_identity().get("Account") | ||
target_account_id = event.get("account_id") | ||
LOGGER.info( | ||
"Target Account Id: %s - This is running in %s. These are the same: %s", | ||
target_account_id, | ||
org_root_account_id, | ||
target_account_id == org_root_account_id, | ||
) | ||
all_regions_enabled = enable_regions_for_account( | ||
boto3.client("account"), | ||
target_account_id, | ||
desired_regions, | ||
org_root_account_id, | ||
) | ||
event["all_regions_enabled"] = all_regions_enabled | ||
|
||
return event |
120 changes: 120 additions & 0 deletions
120
src/lambda_codebase/account_processing/tests/test_configure_account_regions.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,120 @@ | ||
""" | ||
Tests the account alias configuration lambda | ||
""" | ||
|
||
import unittest | ||
import boto3 | ||
from botocore.stub import Stubber | ||
from aws_xray_sdk import global_sdk_config | ||
from ..configure_account_regions import get_regions_from_ssm, enable_regions_for_account | ||
|
||
global_sdk_config.set_sdk_enabled(False) | ||
|
||
|
||
class SuccessTestCase(unittest.TestCase): | ||
def test_get_regions_from_ssm(self): | ||
ssm_client = boto3.client("ssm", region_name="us-east-1") | ||
ssm_stubber = Stubber(ssm_client) | ||
ssm_stubber.add_response("get_parameter", {"Parameter": {"Value": "[1,2,3]"}}) | ||
ssm_stubber.activate() | ||
self.assertListEqual(get_regions_from_ssm(ssm_client), [1, 2, 3]) | ||
|
||
def test_enable_regions_for_account(self): | ||
accounts_client = boto3.client("account", region_name="us-east-1") | ||
account_stubber = Stubber(accounts_client) | ||
account_stubber.add_response( | ||
"list_regions", | ||
{ | ||
"Regions": [ | ||
{"RegionName": "us-east-1", "RegionOptStatus": "ENABLED_BY_DEFAULT"} | ||
] | ||
}, | ||
) | ||
account_stubber.activate() | ||
self.assertTrue( | ||
enable_regions_for_account( | ||
accounts_client, | ||
"123456789", | ||
desired_regions=["us-east-1"], | ||
org_root_account_id="123456789", | ||
) | ||
) | ||
|
||
def test_enable_regions_for_account_with_pagination(self): | ||
accounts_client = boto3.client("account", region_name="us-east-1") | ||
account_stubber = Stubber(accounts_client) | ||
account_stubber.add_response( | ||
"list_regions", | ||
{ | ||
"Regions": [ | ||
{"RegionName": "us-east-1", "RegionOptStatus": "ENABLED_BY_DEFAULT"} | ||
], | ||
"NextToken": "1", | ||
}, | ||
) | ||
account_stubber.add_response( | ||
"list_regions", | ||
{ | ||
"Regions": [ | ||
{"RegionName": "af-south-1", "RegionOptStatus": "DISABLED"} | ||
], | ||
"NextToken": "2", | ||
}, | ||
) | ||
account_stubber.add_response( | ||
"list_regions", | ||
{"Regions": [{"RegionName": "sco-west-1", "RegionOptStatus": "DISABLED"}]}, | ||
) | ||
account_stubber.add_response( | ||
"enable_region", | ||
{}, | ||
{"RegionName": "af-south-1"}, | ||
) | ||
account_stubber.add_response( | ||
"enable_region", | ||
{}, | ||
{"RegionName": "sco-west-1"}, | ||
) | ||
account_stubber.activate() | ||
self.assertFalse( | ||
enable_regions_for_account( | ||
accounts_client, | ||
"123456789", | ||
desired_regions=["us-east-1", "af-south-1", "sco-west-1"], | ||
org_root_account_id="123456789", | ||
) | ||
) | ||
account_stubber.assert_no_pending_responses() | ||
|
||
def test_enable_regions_for_account_that_is_not_current_account(self): | ||
accounts_client = boto3.client("account", region_name="us-east-1") | ||
account_stubber = Stubber(accounts_client) | ||
account_stubber.add_response( | ||
"list_regions", | ||
{ | ||
"Regions": [ | ||
{ | ||
"RegionName": "us-east-1", | ||
"RegionOptStatus": "ENABLED_BY_DEFAULT", | ||
}, | ||
{"RegionName": "sco-west-1", "RegionOptStatus": "DISABLED"}, | ||
] | ||
}, | ||
) | ||
account_stubber.add_response( | ||
"enable_region", | ||
{}, | ||
{ | ||
"RegionName": "sco-west-1", | ||
"AccountId": "123456789", | ||
}, | ||
) | ||
account_stubber.activate() | ||
self.assertFalse( | ||
enable_regions_for_account( | ||
accounts_client, | ||
"123456789", | ||
desired_regions=["us-east-1", "sco-west-1"], | ||
org_root_account_id="987654321", | ||
) | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters