Skip to content

Commit

Permalink
Merge pull request #287 from newscorp-ghfb/release
Browse files Browse the repository at this point in the history
[AUTOMATED] Changes from Sandbox
  • Loading branch information
CarloGiannattasio authored Jun 4, 2024
2 parents 2223249 + 5c06348 commit 71bddc5
Showing 1 changed file with 93 additions and 64 deletions.
157 changes: 93 additions & 64 deletions tools/c7n_org/scripts/orgaccounts.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,34 +8,54 @@
from c7n.resources.aws import ApiStats
from c7n.credentials import assumed_session, SessionFactory
from c7n.utils import yaml_dump
import re

ROLE_TEMPLATE = "arn:aws:iam::{Id}:role/OrganizationAccountAccessRole"
NAME_TEMPLATE = "{name}"

log = logging.getLogger('orgaccounts')
log = logging.getLogger("orgaccounts")


@click.command()
@click.option(
'--role',
"--role",
default=ROLE_TEMPLATE,
help="Role template for accounts in the config, defaults to %s" % ROLE_TEMPLATE)
help="Role template for accounts in the config, defaults to %s" % ROLE_TEMPLATE,
)
@click.option(
'--name',
"--name",
default=NAME_TEMPLATE,
help="Name template for accounts in the config, defaults to %s" % NAME_TEMPLATE)
@click.option('--ou', multiple=True, default=["/"],
help="Only export the given subtrees of an organization")
@click.option('-r', '--regions', multiple=True,
help="If specified, set regions per account in config")
@click.option('--assume', help="Role to assume for Credentials")
@click.option('--profile', help="AWS CLI Profile to use for Credentials")
help="Name template for accounts in the config, defaults to %s" % NAME_TEMPLATE,
)
@click.option(
'-f', '--output', type=click.File('w'),
help="File to store the generated config (default stdout)")
@click.option('-a', '--active', is_flag=True, default=False, help="Get only active accounts")
@click.option('-i', '--ignore', multiple=True,
help="list of accounts that won't be added to the config file")
"--ou",
multiple=True,
default=["/"],
help="Only export the given subtrees of an organization",
)
@click.option(
"-r",
"--regions",
multiple=True,
help="If specified, set regions per account in config",
)
@click.option("--assume", help="Role to assume for Credentials")
@click.option("--profile", help="AWS CLI Profile to use for Credentials")
@click.option(
"-f",
"--output",
type=click.File("w"),
help="File to store the generated config (default stdout)",
)
@click.option(
"-a", "--active", is_flag=True, default=False, help="Get only active accounts"
)
@click.option(
"-i",
"--ignore",
multiple=True,
help="list of accounts that won't be added to the config file",
)
def main(role, name, ou, assume, profile, output, regions, active, ignore):
"""Generate a c7n-org accounts config file using AWS Organizations
Expand All @@ -44,8 +64,8 @@ def main(role, name, ou, assume, profile, output, regions, active, ignore):
"""
logging.basicConfig(level=logging.INFO)

stats, session = get_session(assume, 'c7n-org', profile)
client = session.client('organizations')
stats, session = get_session(assume, "c7n-org", profile)
client = session.client("organizations")
accounts = []
for path in ou:
ou = get_ou_from_path(client, path)
Expand All @@ -55,40 +75,41 @@ def main(role, name, ou, assume, profile, output, regions, active, ignore):
for a in accounts:
tags = []

path_parts = a['Path'].strip('/').split('/')
path_parts = a["Path"].strip("/").split("/")
for idx, _ in enumerate(path_parts):
tags.append("path:/%s" % "/".join(path_parts[:idx + 1]))
tags.append("path:/%s" % "/".join(path_parts[: idx + 1]))

for k, v in a.get('Tags', {}).items():
for k, v in a.get("Tags", {}).items():
tags.append("{}:{}".format(k, v))

a['OrgId'] = a['Arn'].split('/')[1]
if not role.startswith('arn'):
arn_role = "arn:aws:iam::{}:role/{}".format(a['Id'], role)
a["OrgId"] = a["Arn"].split("/")[1]
if not role.startswith("arn"):
arn_role = "arn:aws:iam::{}:role/{}".format(a["Id"], role)
else:
arn_role = role.format(**a)
ainfo = {
'account_id': a['Id'],
'email': a['Email'],
'display_name': a['Name'],
'name': a['Name'],
'org_id': a['OrgId'],
'tags': tags,
'role': arn_role}
ainfo['name'] = name.format(**ainfo)
"account_id": a["Id"],
"email": a["Email"],
"display_name": a["Name"],
"name": a["Name"],
"org_id": a["OrgId"],
"tags": tags,
"role": arn_role,
}
ainfo["name"] = name.format(**ainfo)
if regions:
ainfo['regions'] = list(regions)
if 'Tags' in a and a['Tags']:
ainfo['vars'] = a['Tags']
ainfo["regions"] = list(regions)
if "Tags" in a and a["Tags"]:
ainfo["vars"] = a["Tags"]

results.append(ainfo)

# log.info('api calls {}'.format(stats.get_metadata()))
print(yaml_dump({'accounts': results}), file=output)
print(yaml_dump({"accounts": results}), file=output)


def get_session(role, session_name, profile):
region = os.environ.get('AWS_DEFAULT_REGION', 'eu-west-1')
region = os.environ.get("AWS_DEFAULT_REGION", "eu-west-1")
stats = ApiStats(Bag(), Config.empty())
if role:
s = assumed_session(role, session_name, region=region)
Expand All @@ -99,38 +120,41 @@ def get_session(role, session_name, profile):


def get_ou_from_path(client, path):
ou = client.list_roots()['Roots'][0]
ou = client.list_roots()["Roots"][0]

if path == "/":
ou['Path'] = path
ou["Path"] = path
return ou

ou_pager = client.get_paginator('list_organizational_units_for_parent')
for part in path.strip('/').split('/'):
ou_pager = client.get_paginator("list_organizational_units_for_parent")
for part in path.strip("/").split("/"):
found = False
for page in ou_pager.paginate(ParentId=ou['Id']):
for child in page.get('OrganizationalUnits'):
if child['Name'] == part:
for page in ou_pager.paginate(ParentId=ou["Id"]):
for child in page.get("OrganizationalUnits"):
if child["Name"] == part:
found = True
ou = child
break
if found:
break
if found is False:
raise ValueError(
"No OU named:%r found in path: %s" % (
path, path))
ou['Path'] = path
raise ValueError("No OU named:%r found in path: %s" % (path, path))
ou["Path"] = path
return ou


def get_sub_ous(client, ou):
results = [ou]
ou_pager = client.get_paginator('list_organizational_units_for_parent')
for sub_ou in ou_pager.paginate(
ParentId=ou['Id']).build_full_result().get(
'OrganizationalUnits'):
sub_ou['Path'] = "/%s/%s" % (ou['Path'].strip('/'), sub_ou['Name'])
ignore_regex = r".*Closed Accounts.*"
ou_pager = client.get_paginator("list_organizational_units_for_parent")
for sub_ou in (
ou_pager.paginate(ParentId=ou["Id"])
.build_full_result()
.get("OrganizationalUnits")
):
sub_ou["Path"] = "/%s/%s" % (ou["Path"].strip("/"), sub_ou["Name"])
if re.search(ignore_regex, sub_ou["Path"]):
continue
results.extend(get_sub_ous(client, sub_ou))
return results

Expand All @@ -141,25 +165,30 @@ def get_accounts_for_ou(client, ou, active, recursive=True, ignoredAccounts=()):
if recursive:
ous = get_sub_ous(client, ou)

account_pager = client.get_paginator('list_accounts_for_parent')
account_pager = client.get_paginator("list_accounts_for_parent")
for ou in ous:
for a in account_pager.paginate(
ParentId=ou['Id']).build_full_result().get(
'Accounts', []):
a['Path'] = ou['Path']
a['Tags'] = {
t['Key']: t['Value'] for t in
client.list_tags_for_resource(ResourceId=a['Id']).get('Tags', ())}
if a['Id'] in ignoredAccounts:
for a in (
account_pager.paginate(ParentId=ou["Id"])
.build_full_result()
.get("Accounts", [])
):
a["Path"] = ou["Path"]
a["Tags"] = {
t["Key"]: t["Value"]
for t in client.list_tags_for_resource(ResourceId=a["Id"]).get(
"Tags", ()
)
}
if a["Id"] in ignoredAccounts:
continue

if active:
if a['Status'] == 'ACTIVE':
if a["Status"] == "ACTIVE":
results.append(a)
else:
results.append(a)
return results


if __name__ == '__main__':
if __name__ == "__main__":
main()

0 comments on commit 71bddc5

Please sign in to comment.