diff --git a/CHANGELOG.md b/CHANGELOG.md index f727e6f..d5b2712 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,5 @@ + # Changelog All notable changes to this project will be documented in this file. @@ -6,49 +7,61 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). -## [1.6.2] - 2022-14-09 +## [1.6.3] - 2022-9-10 ### Added - Botocove now has a simple typecheck for the account_ids kwarg to assert a list -has been provided rather than a string. + has been provided rather than a string. + +### Fixed + +- Removed references to `org_master` kwarg and added deprecration warning + +## [1.6.2] - 2022-14-09 + +### Added + +- Botocove now has a simple typecheck for the regions kwarg to assert a list + has been provided rather than a string. ### Fixed - Botocove no longer calls DescribeAccount per account when running in an AWS -organization. + organization. - `org_master` is now a deprecated kwarg: Botocove will optimistically check for -permission -to an AWS Organization to list accounts, and fall back to not adding metadata to -CoveSession. + permission + to an AWS Organization to list accounts, and fall back to not adding metadata to + CoveSession. ## [1.6.1] - 2022-04-03 ### Fixed - Botocove's progress bar in TTY mode is now incremental as each account function -returns rather than blocking on slowest thread in a batch. + returns rather than blocking on slowest thread in a batch. ## [1.6.0] - 2022-04-03 ### Added - Botocove now supports AWS Organizational Unit IDs as a target and ignore ID. Child -accounts and OUs will be recursively discovered and targeted for the passed OU ID. + accounts and OUs will be recursively discovered and targeted for the passed OU + ID. ## [1.5.2] - 2022-23-02 ### Added - Botocove now supports a regions argument, allowing sessions to be run across -one or many regions per account + one or many regions per account ## [1.5.1] - 2022-13-02 ### Fixed - Reverted internal handling of output data to a dictionary rather than dataclass: -issues with recursive copy required to output dataclass as dict. + issues with recursive copy required to output dataclass as dict. ## [1.5.0] - 2022-06-02 @@ -59,7 +72,7 @@ issues with recursive copy required to output dataclass as dict. ### Fixed - Memory leak when running in large organizations: botocove now allows - completed Session objects to be garbage collected + completed Session objects to be garbage collected ## [1.4.1] - 2022-15-01 @@ -78,7 +91,7 @@ issues with recursive copy required to output dataclass as dict. ### Fixed - Fixed missing RoleSessionName when not using Cove's organisation master -session information enrichment + session information enrichment ### Removed @@ -89,21 +102,21 @@ session information enrichment ### Added - Added option to set a custom `RoleSessionName` parameter in -`sts.assume_role()` calls for the `cove` decorator. + `sts.assume_role()` calls for the `cove` decorator. ## [1.3.1] - 2021-27-9 ### Fixed - Fixed bug where a large amount of accounts would cause the AWS DescribeAccount -api to throttle and throw an exception + api to throttle and throw an exception ## [1.3.0] - 2021-07-1 ### Added - Added option to set a custom `RoleSessionName` parameter in -`sts.assume_role()` calls for the `cove` decorator. + `sts.assume_role()` calls for the `cove` decorator. ## [1.2.0] - 2021-26-3 @@ -121,7 +134,7 @@ api to throttle and throw an exception ### Added - Support for assuming roles from accounts that are not AWS Organization masters -via the org_master=False argument. + via the org_master=False argument. - Changelog! - Add full typing and mypy to linting CI - Defensive typing around `ignore_ids` diff --git a/botocove/cove_decorator.py b/botocove/cove_decorator.py index 13b9c2f..f34bbfe 100644 --- a/botocove/cove_decorator.py +++ b/botocove/cove_decorator.py @@ -1,6 +1,7 @@ import functools import logging -from typing import Any, Callable, List, Optional +from typing import Any, Callable, Dict, List, Optional +from warnings import warn from boto3.session import Session from mypy_boto3_sts.type_defs import PolicyDescriptorTypeTypeDef @@ -23,15 +24,19 @@ def cove( policy_arns: Optional[List[PolicyDescriptorTypeTypeDef]] = None, assuming_session: Optional[Session] = None, raise_exception: bool = False, - org_master: bool = True, thread_workers: int = 20, regions: Optional[List[str]] = None, + **cove_kwargs: Any, ) -> Callable: def decorator(func: Callable[..., Any]) -> Callable[..., CoveOutput]: @functools.wraps(func) def wrapper(*args: Any, **kwargs: Any) -> CoveOutput: + _check_deprecation(cove_kwargs) + _typecheck_regions(regions) + _typecheck_id_list(target_ids) + _typecheck_id_list(ignore_ids) host_account = CoveHostAccount( target_ids=target_ids, @@ -40,7 +45,6 @@ def wrapper(*args: Any, **kwargs: Any) -> CoveOutput: role_session_name=role_session_name, policy=policy, policy_arns=policy_arns, - org_master=org_master, assuming_session=assuming_session, thread_workers=thread_workers, regions=regions, @@ -91,3 +95,37 @@ def _typecheck_regions(list_of_regions: Optional[List[str]]) -> None: raise TypeError( f"regions must be a list of str. Got str {repr(list_of_regions)}." ) + + +def _check_deprecation(kwargs: Dict[str, Any]) -> None: + if "org_master" in kwargs: + warn( + "org_master is a deprecated kwarg since Cove 1.6.2 and has no effect", + DeprecationWarning, + stacklevel=2, + ) + _raise_type_error_for_any_kwarg_except_org_master(kwargs) + return None + + +def _raise_type_error_for_any_kwarg_except_org_master(kwargs: Dict[str, Any]) -> None: + for key in kwargs: + if key != "org_master": + raise TypeError(f"Cove() got an unexpected keyword argument '{key}'") + return None + + +def _typecheck_id_list(list_of_ids: Optional[List[str]]) -> None: + if list_of_ids is None: + return + for _id in list_of_ids: + _typecheck_id(_id) + + +def _typecheck_id(_id: str) -> None: + if isinstance(_id, str): + return + raise TypeError( + f"{_id} is an incorrect type: all account and ou id's must be strings " + f"not {type(_id)}" + ) diff --git a/botocove/cove_host_account.py b/botocove/cove_host_account.py index 936e606..9892718 100644 --- a/botocove/cove_host_account.py +++ b/botocove/cove_host_account.py @@ -48,7 +48,6 @@ def __init__( policy: Optional[str], policy_arns: Optional[List[PolicyDescriptorTypeTypeDef]], assuming_session: Optional[Session], - org_master: bool, thread_workers: int, regions: Optional[List[str]], ) -> None: @@ -90,8 +89,6 @@ def __init__( self.policy = policy self.policy_arns = policy_arns - self.org_master = org_master - def get_cove_sessions(self) -> List[CoveSessionInformation]: logger.info(f"Getting session information for {self.target_accounts=}") logger.info(f"Role: {self.role_to_assume=} {self.role_session_name=}") diff --git a/botocove/cove_runner.py b/botocove/cove_runner.py index 4fe7a5f..48be2cd 100644 --- a/botocove/cove_runner.py +++ b/botocove/cove_runner.py @@ -67,7 +67,6 @@ def cove_thread( cove_session = CoveSession( account_session_info, sts_client=self.host_account.sts_client, - org_master=self.host_account.org_master, ) try: cove_session.activate_cove_session() diff --git a/botocove/cove_session.py b/botocove/cove_session.py index 206c611..d8b9adf 100644 --- a/botocove/cove_session.py +++ b/botocove/cove_session.py @@ -24,10 +24,8 @@ def __init__( self, session_info: CoveSessionInformation, sts_client: STSClient, - org_master: bool, ) -> None: self.session_information = session_info - self.org_master = org_master self.sts_client = sts_client def __repr__(self) -> str: diff --git a/pyproject.toml b/pyproject.toml index 2d7d7bd..3bb3092 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "botocove" -version = "1.6.2" +version = "1.6.3" description = "A decorator to allow running a function against all AWS accounts in an organization" authors = ["Dave Connell "] license = "LGPL-3.0-or-later" diff --git a/tests/moto_mock_org/test_host_account/test_resolve_targets.py b/tests/moto_mock_org/test_host_account/test_resolve_targets.py index 777bde1..a38bf82 100644 --- a/tests/moto_mock_org/test_host_account/test_resolve_targets.py +++ b/tests/moto_mock_org/test_host_account/test_resolve_targets.py @@ -11,7 +11,6 @@ def test_target_all_in_org(mock_small_org: SmallOrg) -> None: role_session_name=None, policy=None, policy_arns=None, - org_master=True, assuming_session=None, regions=None, thread_workers=20, @@ -33,7 +32,6 @@ def test_target_all_in_org_ignore_one(mock_small_org: SmallOrg) -> None: role_session_name=None, policy=None, policy_arns=None, - org_master=True, assuming_session=None, regions=None, thread_workers=20, @@ -57,7 +55,6 @@ def test_target_all_in_org_ignore_one_ou(mock_small_org: SmallOrg) -> None: role_session_name=None, policy=None, policy_arns=None, - org_master=True, assuming_session=None, regions=None, thread_workers=20, @@ -84,7 +81,6 @@ def test_large_org_target_all_in_org_ignore_one_ou(mock_large_org: LargeOrg) -> role_session_name=None, policy=None, policy_arns=None, - org_master=True, assuming_session=None, regions=None, thread_workers=20, @@ -112,7 +108,6 @@ def test_large_org_target_all_in_org_ignore_two_ous(mock_large_org: LargeOrg) -> role_session_name=None, policy=None, policy_arns=None, - org_master=True, assuming_session=None, regions=None, thread_workers=20, @@ -139,7 +134,6 @@ def test_target_targets_and_ignores(mock_small_org: SmallOrg) -> None: role_session_name=None, policy=None, policy_arns=None, - org_master=True, assuming_session=None, regions=None, thread_workers=20, @@ -162,7 +156,6 @@ def test_target_just_targets(mock_small_org: SmallOrg) -> None: role_session_name=None, policy=None, policy_arns=None, - org_master=True, assuming_session=None, regions=None, thread_workers=20, diff --git a/tests/moto_mock_org/test_moto.py b/tests/moto_mock_org/test_moto.py index 2e13344..7bb09f0 100644 --- a/tests/moto_mock_org/test_moto.py +++ b/tests/moto_mock_org/test_moto.py @@ -10,7 +10,6 @@ def test_small_org(mock_small_org: SmallOrg) -> None: role_session_name=None, policy=None, policy_arns=None, - org_master=True, assuming_session=None, regions=None, thread_workers=20, @@ -28,7 +27,6 @@ def test_large_org(mock_large_org: LargeOrg) -> None: role_session_name=None, policy=None, policy_arns=None, - org_master=True, assuming_session=None, regions=None, thread_workers=20, diff --git a/tests/test_decorator.py b/tests/test_decorator.py index e30fbe7..c07a0af 100644 --- a/tests/test_decorator.py +++ b/tests/test_decorator.py @@ -99,7 +99,6 @@ def test_decorated_simple_func_passed_session_name( @cove( assuming_session=mock_session, role_session_name=session_name, - org_master=False, ) def simple_func(session: CoveSession) -> Optional[str]: return session.session_information["RoleSessionName"] @@ -118,7 +117,6 @@ def test_decorated_simple_func_passed_policy( @cove( assuming_session=mock_session, policy=session_policy, - org_master=False, ) def simple_func(session: CoveSession) -> Optional[str]: return session.session_information["Policy"] @@ -139,7 +137,6 @@ def test_decorated_simple_func_passed_policy_arn( @cove( assuming_session=mock_session, policy_arns=session_policy_arns, - org_master=False, ) def simple_func( session: CoveSession,