Skip to content

Commit 5462c73

Browse files
committed
Split the account-wide delgations done via Organizations from the regional GuardDuty delegation
1 parent 0a7d2d7 commit 5462c73

File tree

3 files changed

+142
-45
lines changed

3 files changed

+142
-45
lines changed

Diff for: org-delegation/README.md

+31-3
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# Organizations Delegated Access
22

3-
This script will configure Delegated Administrator in a payer account for GuardDuty and IAM Access Analyzer.
3+
These scripts will configure Delegated Administrator in a payer account for GuardDuty and IAM Access Analyzer.
44

55
## Why?
66

@@ -9,9 +9,9 @@ This script will configure Delegated Administrator in a payer account for GuardD
99
The concept of Delegated Admin accounts for specific services is new. It allows the Organization master to grant an account in the organization full ability to deploy and manage the service in all accounts in the Organization. This eliminates the need for teams to login the payer account or individually deploy tooling in an organization's child accounts.
1010

1111

12-
## What the script does.
12+
## What the delegate-admin script does.
1313

14-
This script will enable delegated admin for GuardDuty and IAM Access Analyzer (plus any future services).
14+
This script will enable delegated admin for IAM Access Analyzer (plus any future services).
1515

1616
Then, because GuardDuty has to be special, the script iterates through all the regions returned by ec2:DescribeRegions. It will then call enable_organization_admin_account() to configure GuardDuty's delegated admin.
1717

@@ -38,6 +38,34 @@ optional arguments:
3838

3939
You must specify `--actually-do-it` for the changes to be made. Otherwise the script runs in dry-run mode only.
4040

41+
## What the delegate-guardduty script does.
42+
43+
This script iterates through all the regions returned by ec2:DescribeRegions. It will then call enable_organization_admin_account() to configure GuardDuty's delegated admin.
44+
45+
The script will report if the organization has delegated to another child account, or if the delegation was already configured before attempting to enable account delegation.
46+
47+
## Usage
48+
49+
```bash
50+
usage: delegate-guardduty.py [-h] [--debug] [--error] [--timestamp]
51+
[--region REGION] [--profile PROFILE]
52+
[--actually-do-it] [--delegated-admin ADMIN_ACCOUNT_ID]
53+
54+
optional arguments:
55+
-h, --help show this help message and exit
56+
--debug print debugging info
57+
--error print error info only
58+
--timestamp Output log with timestamp and toolname
59+
--region REGION Only Process Specified Region
60+
--profile PROFILE Use this CLI profile (instead of default or env credentials)
61+
--actually-do-it Actually Perform the action
62+
--delegated-admin ADMIN_ACCOUNT_ID
63+
Account that the payer will delegate access to
64+
```
65+
66+
You must specify `--actually-do-it` for the changes to be made. Otherwise the script runs in dry-run mode only.
67+
68+
4169

4270
## AWS Docs
4371

Diff for: org-delegation/delegate-admin.py

+4-42
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99

1010
services = {
1111
"access-analyzer.amazonaws.com": "IAM Access Analyzer",
12-
"guardduty.amazonaws.com": "AWS GuardDuty",
12+
# "guardduty.amazonaws.com": "AWS GuardDuty", # apparently this isn't a proper
1313
}
1414

1515

@@ -29,11 +29,11 @@ def main(args, logger):
2929
response = org_client.list_delegated_administrators(ServicePrincipal=service)
3030
if len(response['DelegatedAdministrators']) == 1:
3131
if response['DelegatedAdministrators'][0]['Id'] == args.accountId:
32-
logger.debug(f"{args.accountId} is already the delegated admin for {description}")
32+
logger.info(f"{args.accountId} is already the delegated admin for {description}")
3333
else:
3434
logger.error(f"{response['DelegatedAdministrators'][0]['Id']} is the delegated admin for {service}. Not performing the update")
3535
elif len(response['DelegatedAdministrators']) > 1:
36-
logger.erro(f"Multiple delegated admin accounts for {service}. Cannot safely proceed.")
36+
logger.error(f"Multiple delegated admin accounts for {service}. Cannot safely proceed.")
3737
elif args.actually_do_it is True:
3838
# Safe to Proceed
3939
logger.info(f"Enabling {description} Delegation to {args.accountId}")
@@ -44,44 +44,6 @@ def main(args, logger):
4444
else:
4545
logger.info(f"Would enable {description} Delegation to {args.accountId}")
4646

47-
# For some reason GuardDuty also needs to be enabled Regionally. Gah!
48-
for r in get_regions(session, args):
49-
guardduty_client = session.client("guardduty", region_name=r)
50-
response = guardduty_client.list_organization_admin_accounts()
51-
if len(response['AdminAccounts']) > 1:
52-
logger.error(f"too many admin accounts in region {r}. Cannot proceed.")
53-
elif len(response['AdminAccounts']) == 1:
54-
if response['AdminAccounts'][0]['AdminAccountId'] == args.accountId:
55-
logger.debug(f"Account {args.accountId} is already the delegated admin for region {r} and in state {response['AdminAccounts'][0]['AdminStatus']}")
56-
else:
57-
logger.error(f"{response['AdminAccounts'][0]['AdminAccountId']} is already the delegated admin in {r}. Not performing update")
58-
elif args.actually_do_it is True:
59-
try:
60-
logger.info(f"Enablng GuardDuty Delegated Admin to {args.accountId} in region {r}")
61-
guardduty_client.enable_organization_admin_account(AdminAccountId=args.accountId)
62-
except ClientError as e:
63-
logger.critical(e)
64-
else:
65-
logger.info(f"Would enable GuardDuty Delegated Admin to {args.accountId} in region {r}")
66-
67-
def get_regions(session, args):
68-
'''Return a list of regions with us-east-1 first. If --region was specified, return a list wth just that'''
69-
70-
# If we specifed a region on the CLI, return a list of just that
71-
if args.region:
72-
return([args.region])
73-
74-
# otherwise return all the regions, us-east-1 first
75-
ec2 = session.client('ec2')
76-
response = ec2.describe_regions()
77-
output = ['us-east-1']
78-
for r in response['Regions']:
79-
# return us-east-1 first, but dont return it twice
80-
if r['RegionName'] == "us-east-1":
81-
continue
82-
output.append(r['RegionName'])
83-
return(output)
84-
8547
def do_args():
8648
import argparse
8749
parser = argparse.ArgumentParser()
@@ -103,7 +65,7 @@ def do_args():
10365

10466
# Logging idea stolen from: https://docs.python.org/3/howto/logging.html#configuring-logging
10567
# create console handler and set level to debug
106-
logger = logging.getLogger('enable-guardduty')
68+
logger = logging.getLogger('delegated-admin')
10769
ch = logging.StreamHandler()
10870
if args.debug:
10971
logger.setLevel(logging.DEBUG)

Diff for: org-delegation/delegate-guardduty.py

+107
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
#!/usr/bin/env python3
2+
3+
import boto3
4+
from botocore.exceptions import ClientError
5+
# from botocore.errorfactory import BadRequestException
6+
import os
7+
import logging
8+
# logger = logging.getLogger()
9+
10+
11+
12+
def main(args, logger):
13+
'''Executes the Primary Logic of the Fast Fix'''
14+
15+
# If they specify a profile use it. Otherwise do the normal thing
16+
if args.profile:
17+
session = boto3.Session(profile_name=args.profile)
18+
else:
19+
session = boto3.Session()
20+
21+
# GuardDuty needs to be enabled Regionally. Gah!
22+
for r in get_regions(session, args):
23+
guardduty_client = session.client("guardduty", region_name=r)
24+
response = guardduty_client.list_organization_admin_accounts()
25+
if len(response['AdminAccounts']) > 1:
26+
logger.error(f"too many admin accounts in region {r}. Cannot proceed.")
27+
elif len(response['AdminAccounts']) == 1:
28+
if response['AdminAccounts'][0]['AdminAccountId'] == args.accountId:
29+
logger.debug(f"Account {args.accountId} is already the delegated admin for region {r} and in state {response['AdminAccounts'][0]['AdminStatus']}")
30+
else:
31+
logger.error(f"{response['AdminAccounts'][0]['AdminAccountId']} is already the delegated admin in {r}. Not performing update")
32+
elif args.actually_do_it is True:
33+
try:
34+
logger.info(f"Enablng GuardDuty Delegated Admin to {args.accountId} in region {r}")
35+
guardduty_client.enable_organization_admin_account(AdminAccountId=args.accountId)
36+
except ClientError as e:
37+
logger.critical(e)
38+
else:
39+
logger.info(f"Would enable GuardDuty Delegated Admin to {args.accountId} in region {r}")
40+
41+
def get_regions(session, args):
42+
'''Return a list of regions with us-east-1 first. If --region was specified, return a list wth just that'''
43+
44+
# If we specifed a region on the CLI, return a list of just that
45+
if args.region:
46+
return([args.region])
47+
48+
# otherwise return all the regions, us-east-1 first
49+
ec2 = session.client('ec2')
50+
response = ec2.describe_regions()
51+
output = ['us-east-1']
52+
for r in response['Regions']:
53+
# return us-east-1 first, but dont return it twice
54+
if r['RegionName'] == "us-east-1":
55+
continue
56+
output.append(r['RegionName'])
57+
return(output)
58+
59+
def do_args():
60+
import argparse
61+
parser = argparse.ArgumentParser()
62+
parser.add_argument("--debug", help="print debugging info", action='store_true')
63+
parser.add_argument("--error", help="print error info only", action='store_true')
64+
parser.add_argument("--timestamp", help="Output log with timestamp and toolname", action='store_true')
65+
parser.add_argument("--region", help="Only Process Specified Region")
66+
parser.add_argument("--profile", help="Use this CLI profile (instead of default or env credentials)")
67+
parser.add_argument("--actually-do-it", help="Actually Perform the action", action='store_true')
68+
parser.add_argument("--delegated-admin", dest='accountId', help="Delegate access to this account id", required=True)
69+
70+
args = parser.parse_args()
71+
72+
return(args)
73+
74+
if __name__ == '__main__':
75+
76+
args = do_args()
77+
78+
# Logging idea stolen from: https://docs.python.org/3/howto/logging.html#configuring-logging
79+
# create console handler and set level to debug
80+
logger = logging.getLogger('enable-guardduty')
81+
ch = logging.StreamHandler()
82+
if args.debug:
83+
logger.setLevel(logging.DEBUG)
84+
elif args.error:
85+
logger.setLevel(logging.ERROR)
86+
else:
87+
logger.setLevel(logging.INFO)
88+
89+
# Silence Boto3 & Friends
90+
logging.getLogger('botocore').setLevel(logging.WARNING)
91+
logging.getLogger('boto3').setLevel(logging.WARNING)
92+
logging.getLogger('urllib3').setLevel(logging.WARNING)
93+
94+
# create formatter
95+
if args.timestamp:
96+
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
97+
else:
98+
formatter = logging.Formatter('%(levelname)s - %(message)s')
99+
# add formatter to ch
100+
ch.setFormatter(formatter)
101+
# add ch to logger
102+
logger.addHandler(ch)
103+
104+
try:
105+
main(args, logger)
106+
except KeyboardInterrupt:
107+
exit(1)

0 commit comments

Comments
 (0)