Skip to content

Commit

Permalink
Release 1.6.0
Browse files Browse the repository at this point in the history
  • Loading branch information
connelldave committed Mar 4, 2022
1 parent 8b2cff3 commit f6d52f8
Show file tree
Hide file tree
Showing 4 changed files with 60 additions and 26 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,11 @@ 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.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.
## [1.5.2] - 2022-23-02
### Added
- Botocove now supports a regions argument, allowing sessions to be run across
Expand Down
31 changes: 18 additions & 13 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,21 +1,21 @@
# Botocove

Run a function against a selection of AWS accounts, or all AWS accounts in an
organization, concurrently with thread safety. Run in one or multiple regions.
Run a function against a selection of AWS accounts, Organizational Units (OUs)
or all AWS accounts in an organization, concurrently with thread safety.
Run in one or multiple regions.

By default, opinionated to work with the standard
AWS Organization master/member configuration from an organization master
account.
Opinionated by default to work with the standard AWS Organization master/member
configuration from an organization master account but customisable for any context.

- Fast
- Easy
- Dolphin Themed 🐬

A simple decorator for functions to remove time and complexity burden. Uses
Botocove is a simple decorator for functions to remove time and complexity burden. Uses
`ThreadPoolExecutor` to run boto3 sessions against AWS accounts concurrently.

Decorating a function in `@cove` provides a boto3 session to the function and runs it
in every account required, gathering all results into a dictionary.
Decorating a function in `@cove` provides a boto3 session to the decorated Python
function and runs it in every account requested, gathering all results into a dictionary.

**Warning**: this tool gives you the potential to make dangerous changes
at scale. **Test carefully and make idempotent changes**! Please read available
Expand Down Expand Up @@ -100,7 +100,7 @@ def main():
Here's an example of a more customised Cove decorator:
```
@cove(
target_ids=["123456789101", "234567891011"],
target_ids=["123456789101", "234567891011"], # also accepts OU ids!
rolename="AWSControlTowerExecution",
raise_exception=True,
regions=["eu-west-1", "eu-west-2", "us-east-1"],
Expand All @@ -125,13 +125,18 @@ Equivalent to:

`target_ids`: List[str]

A list of AWS accounts and/or AWS Organization Units as strings to attempt to assume role in to. When unset,
default attempts to use every available account ID in an AWS organization. When specifing ou's it will recursivly fetch all child ou's as well.
A list of AWS account IDs and/or AWS Organization Units IDs to attempt to assume role
in to. When unset, attempts to use every available account ID in an AWS organization.
When specifing target OU's, all child OUs and accounts belonging to that OU will be
collected.

`ignore_ids`: List[str]

A list of AWS account ID's that will not attempt assumption in to. Allows IDs to
be ignored.
A list of AWS account ID's and OU's to prevent functions being run by Cove. Ignored IDs
takes precedence over `target_ids`. Providing an OU ID will collect
all child OUs and accounts to ignore.

The calling account that is running the Cove-wrapped function at runtime is always ignored.

`rolename`: str

Expand Down
48 changes: 36 additions & 12 deletions botocove/cove_host_account.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,24 @@
import logging
import re
from typing import Any, Iterable, List, Literal, Optional, Sequence, Set, Tuple, Union
from functools import lru_cache
from typing import (
Any,
Iterable,
List,
Literal,
Optional,
Sequence,
Set,
Tuple,
Union,
cast,
)

import boto3
from boto3.session import Session
from botocore.config import Config
from mypy_boto3_organizations.client import OrganizationsClient
from mypy_boto3_organizations.type_defs import ListChildrenResponseTypeDef
from mypy_boto3_sts.client import STSClient

from botocove.cove_types import CoveSessionInformation
Expand Down Expand Up @@ -193,12 +206,9 @@ def _get_all_accounts_by_organization_units(
return account_list

def _get_all_child_ous(self, parent_ou: str, ou_list: List[str]) -> None:

child_ous = (
self.org_client.get_paginator("list_children")
.paginate(ChildType="ORGANIZATIONAL_UNIT", ParentId=parent_ou)
.build_full_result()
)
"""Depth-first recursion mutates the current_ou_list present in the calling
function to establish all children of a parent OU"""
child_ous = self._get_child_ous(parent_ou)
child_ous_list = [ou["Id"] for ou in child_ous["Children"]]
ou_list.extend(child_ous_list)

Expand All @@ -212,11 +222,7 @@ def _get_accounts_by_organization_units(
account_list: List[str] = []

for ou in organization_units:
ou_children = (
self.org_client.get_paginator("list_children")
.paginate(ChildType="ACCOUNT", ParentId=ou)
.build_full_result()
)
ou_children = self._get_child_accounts(ou)
account_list.extend(acc["Id"] for acc in ou_children["Children"])

return account_list
Expand All @@ -228,3 +234,21 @@ def _get_active_org_accounts(self) -> Set[str]:
.build_full_result()["Accounts"]
)
return {acc["Id"] for acc in all_org_accounts if acc["Status"] == "ACTIVE"}

@lru_cache()
def _get_child_ous(self, parent_ou: str) -> ListChildrenResponseTypeDef:
return cast(
ListChildrenResponseTypeDef,
self.org_client.get_paginator("list_children")
.paginate(ChildType="ORGANIZATIONAL_UNIT", ParentId=parent_ou)
.build_full_result(),
)

@lru_cache()
def _get_child_accounts(self, parent_ou: str) -> ListChildrenResponseTypeDef:
return cast(
ListChildrenResponseTypeDef,
self.org_client.get_paginator("list_children")
.paginate(ChildType="ACCOUNT", ParentId=parent_ou)
.build_full_result(),
)
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[tool.poetry]
name = "botocove"
version = "1.5.2"
version = "1.6.0"
description = "A decorator to allow running a function against all AWS accounts in an organization"
authors = ["Dave Connell <[email protected]>"]
license = "LGPL-3.0-or-later"
Expand Down

0 comments on commit f6d52f8

Please sign in to comment.