diff --git a/src/lambda_codebase/account_processing/configure_account_alias.py b/src/lambda_codebase/account_processing/configure_account_alias.py index 8c815b77e..243497166 100644 --- a/src/lambda_codebase/account_processing/configure_account_alias.py +++ b/src/lambda_codebase/account_processing/configure_account_alias.py @@ -17,9 +17,19 @@ AWS_PARTITION = os.getenv("AWS_PARTITION") +def delete_account_aliases(account, iam_client, current_aliases): + for alias in current_aliases: + LOGGER.info( + "Account %s, removing alias %s", + account.get('account_full_name'), + alias, + ) + iam_client.delete_account_alias(AccountAlias=alias) + + def create_account_alias(account, iam_client): LOGGER.info( - "Ensuring Account: %s has alias %s", + "Adding alias to: %s alias %s", account.get('account_full_name'), account.get('alias'), ) @@ -28,11 +38,33 @@ def create_account_alias(account, iam_client): except iam_client.exceptions.EntityAlreadyExistsException as error: LOGGER.error( f"The account alias {account.get('alias')} already exists." - "The account alias must be unique across all Amazon Web Services products." - "Refer to https://docs.aws.amazon.com/IAM/latest/UserGuide/console_account-alias.html#AboutAccountAlias" + "The account alias must be unique across all Amazon Web Services " + "products. Refer to " + "https://docs.aws.amazon.com/IAM/latest/UserGuide/" + "console_account-alias.html#AboutAccountAlias" ) raise error - return account + + +def ensure_account_has_alias(account, iam_client): + LOGGER.info( + "Ensuring Account: %s has alias %s", + account.get('account_full_name'), + account.get('alias'), + ) + current_aliases = iam_client.list_account_aliases().get('AccountAliases') + if account.get('alias') in current_aliases: + LOGGER.info( + "Account: %s already has alias %s", + account.get('account_full_name'), + account.get('alias'), + ) + return + + # Since you can only have one alias per account, lets + # remove all old aliases (is at most one) + delete_account_aliases(account, iam_client, current_aliases) + create_account_alias(account, iam_client) def lambda_handler(event, _): @@ -43,7 +75,7 @@ def lambda_handler(event, _): f"arn:{AWS_PARTITION}:iam::{account_id}:role/{ADF_ROLE_NAME}", "adf_account_alias_config", ) - create_account_alias(event, role.client("iam")) + ensure_account_has_alias(event, role.client("iam")) else: LOGGER.info( "Account: %s does not need an alias", diff --git a/src/lambda_codebase/account_processing/tests/test_account_alias.py b/src/lambda_codebase/account_processing/tests/test_account_alias.py index 9143cb6ae..63e90158c 100644 --- a/src/lambda_codebase/account_processing/tests/test_account_alias.py +++ b/src/lambda_codebase/account_processing/tests/test_account_alias.py @@ -6,26 +6,62 @@ import boto3 from botocore.stub import Stubber from botocore.exceptions import ClientError +from mock import Mock from aws_xray_sdk import global_sdk_config -from ..configure_account_alias import create_account_alias +from ..configure_account_alias import ( + create_account_alias, + ensure_account_has_alias, +) global_sdk_config.set_sdk_enabled(False) + class SuccessTestCase(unittest.TestCase): - # pylint: disable=W0106 - def test_account_alias(self): + @staticmethod + def test_account_alias_exists_already(): test_account = {"account_id": 123456789012, "alias": "MyCoolAlias"} - iam_client = boto3.client("iam") - stubber = Stubber(iam_client) - create_alias_response = {} - stubber.add_response( - "create_account_alias", create_alias_response, {"AccountAlias": "MyCoolAlias"} - ), - stubber.activate() + iam_client = Mock() + iam_client.list_account_aliases.return_value = { + "AccountAliases": ["MyCoolAlias"], + } + + ensure_account_has_alias(test_account, iam_client) + iam_client.list_account_aliases.assert_called_once_with() + iam_client.delete_account_alias.assert_not_called() + iam_client.create_account_alias.assert_not_called() + + @staticmethod + def test_account_alias_another_alias_exists(): + test_account = {"account_id": 123456789012, "alias": "MyCoolAlias"} + iam_client = Mock() + iam_client.list_account_aliases.return_value = { + "AccountAliases": ["AnotherCoolAlias"], + } + + ensure_account_has_alias(test_account, iam_client) + iam_client.list_account_aliases.assert_called_once_with() + iam_client.delete_account_alias.assert_called_once_with( + AccountAlias='AnotherCoolAlias', + ) + iam_client.create_account_alias.assert_called_once_with( + AccountAlias='MyCoolAlias', + ) - response = create_account_alias(test_account, iam_client) + @staticmethod + def test_account_alias_no_aliases_yet(): + test_account = {"account_id": 123456789012, "alias": "MyCoolAlias"} + iam_client = Mock() + iam_client.list_account_aliases.return_value = { + "AccountAliases": [], + } + + ensure_account_has_alias(test_account, iam_client) + iam_client.list_account_aliases.assert_called_once_with() + iam_client.delete_account_alias.assert_not_called() + iam_client.create_account_alias.assert_called_once_with( + AccountAlias='MyCoolAlias', + ) - self.assertEqual(response, test_account) class FailureTestCase(unittest.TestCase): # pylint: disable=W0106 diff --git a/src/template.yml b/src/template.yml index ea3cb80a5..f85fbf1f0 100644 --- a/src/template.yml +++ b/src/template.yml @@ -661,7 +661,7 @@ Resources: "ErrorEquals": ["States.TaskFailed"], "IntervalSeconds": 3, "BackoffRate": 1.5, - "MaxAttempts": 30 + "MaxAttempts": 10 }, { "ErrorEquals": [ "Lambda.Unknown", @@ -685,7 +685,7 @@ Resources: "ErrorEquals": ["States.TaskFailed"], "IntervalSeconds": 3, "BackoffRate": 1.5, - "MaxAttempts": 30 + "MaxAttempts": 10 }, { "ErrorEquals": [ "Lambda.Unknown", @@ -714,7 +714,7 @@ Resources: "ErrorEquals": ["States.TaskFailed"], "IntervalSeconds": 3, "BackoffRate": 1.5, - "MaxAttempts": 30 + "MaxAttempts": 10 }, { "ErrorEquals": [ "Lambda.Unknown", @@ -738,7 +738,7 @@ Resources: "ErrorEquals": ["States.TaskFailed"], "IntervalSeconds": 3, "BackoffRate": 1.5, - "MaxAttempts": 30 + "MaxAttempts": 10 }, { "ErrorEquals": [ "Lambda.Unknown", @@ -762,7 +762,7 @@ Resources: "ErrorEquals": ["States.TaskFailed"], "IntervalSeconds": 3, "BackoffRate": 1.5, - "MaxAttempts": 30 + "MaxAttempts": 10 }, { "ErrorEquals": [ "Lambda.Unknown", @@ -797,7 +797,7 @@ Resources: "ErrorEquals": ["States.TaskFailed"], "IntervalSeconds": 3, "BackoffRate": 1.5, - "MaxAttempts": 30 + "MaxAttempts": 10 }, { "ErrorEquals": [ "Lambda.Unknown",