Search code examples
amazon-web-servicesboto3aws-organizations

Boto3 list all accounts in an organization from member account


I have 2 accounts:

  • Account A (Master/Management account)
  • Account B (Member account)

I deployed a Lambda function in Account B(Member account) that lists all the accounts in the organization. Below is the Lambda function code:

import boto3
import json

org_client = boto3.client('organizations')
org_id='o-ib1qmgzn48'

def lambda_handler(event, context):
    account_list = []
    def list_aws_accounts_for_ou(ou_id):
        # add accounts in the current/root OU
        try:
            lafp_paginator = org_client.get_paginator('list_accounts_for_parent')
            lafp_page_iterator = lafp_paginator.paginate(ParentId=org_id)
            for page in lafp_page_iterator:
                for acct in page['Accounts']:
                    account_list.append(acct)
        except org_client.exceptions.ClientError as error:
            print(f'Error: {error}')

        # add accounts in child ous
        try:
            lc_paginator = org_client.get_paginator('list_children')
            ou_page_iterator = lc_paginator.paginate(
                ParentId=org_id,
                ChildType='ORGANIZATIONAL_UNIT'
            )
            for page in ou_page_iterator:
                for child in page['Children']:
                    print(f"Adding accounts from child OU {child['Id']}")
                    account_list += list_aws_accounts_for_ou(child['Id'])
        except org_client.exceptions.ClientError as error:
            print(f'Error: {error}')

        print(f"Found {len(account_list)} accounts in Oraganization {org_id}")

    return json.loads(json.dumps(account_list, default=str))

This Lambda function has a role(GetAccountsListLambdaRole) with the below permissions: Basic Lambda execution role An inline policy that assumes a role in Account A(Management/Master account)

{
    "Version": "2012-10-17",
    "Statement": {
        "Effect": "Allow",
        "Action": "sts:AssumeRole",
        "Resource": "arn:aws:iam::Account A:role/OrganizationsReadAssumeRole"
    }
}

The 'OrganizationsReadAssumeRole' in Account A has the following permissions and trust policy:

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "VisualEditor0",
            "Effect": "Allow",
            "Action": [
                "organizations:ListAccounts",
                "organizations:ListAccountsForParent",
                "organizations:ListChildren"
            ],
            "Resource": "*"
        }
    ]
}

The trust policy is:

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Principal": {
                "AWS": "arn:aws:iam::Account B:role/GetAccountsListLambdaRole"
            },
            "Action": "sts:AssumeRole",
            "Condition": {}
        }
    ]
}

When I run the Lambda function, I'm getting the below errors:

Error: An error occurred (AccessDeniedException) when calling the ListAccountsForParent operation: You don't have permissions to access this resource.
Error: An error occurred (AccessDeniedException) when calling the ListChildren operation: You don't have permissions to access this resource.

I have a couple of questions here:

  1. Whatever I'm trying - getting an account list from a member account; is it possible? If yes, how can I resolve the above errors and achieve this?

Please help.


Solution

  • You need to actually assume the OrganizationsReadAssumeRole role in the lambda. This blog has an example:

    sts_connection = boto3.client('sts')
    acct_a = sts_connection.assume_role( 
        RoleArn="arn:aws:iam::222222222222:role/OrganizationsReadAssumeRole",
        RoleSessionName="cross_acct_lambda"
    )
    
    ACCESS_KEY = acct_a['Credentials']['AccessKeyId']
    SECRET_KEY = acct_a['Credentials']['SecretAccessKey']
    SESSION_TOKEN = acct_a['Credentials']['SessionToken']
    
    # create service client using the assumed role credentials, e.g. S3
    org_client = boto3.client(
        'organizations',
        aws_access_key_id=ACCESS_KEY,
        aws_secret_access_key=SECRET_KEY,
        aws_session_token=SESSION_TOKEN,
    )
    ... # rest of your code
    

    Replace 222222222222 with the AWS account ID of the cross-account role that your function is assuming.