Как вы используете "NextToken" в вызовах API AWS

Я столкнулся с небольшой проблемой, которую я действительно изо всех сил пытаюсь понять, как это работает. У меня есть инструмент, который я пишу, который в основном описывает организацию для сбора всех учетных записей в нашей организации AWS. Согласно документации здесь говорится, что он отвечает json учетных записей, которые в моем случае будут сотни и сотни учетных записей. Поэтому я написал очень простой код для переключения ролей в нашу главную учетную запись и выполнения вызова:

import boto3
import uuid
import pprint

iam_client = boto3.client('iam')
sts_client = boto3.client('sts')
org_client = boto3.client('organizations')


print("Starting in account: %s" % sts_client.get_caller_identity().get('Account'))

assumedRoleObject = sts_client.assume_role(
    RoleArn="arn:aws:iam::123456xxx:role/MsCrossAccountAccessRole",
    RoleSessionName="MasterPayer"
)

credentials = assumedRoleObject['Credentials']

org_client = boto3.client(
    'organizations',
    aws_access_key_id = credentials['AccessKeyId'],
    aws_secret_access_key = credentials['SecretAccessKey'],
    aws_session_token = credentials['SessionToken'],
)

getListAccounts = org_client.list_accounts(
    NextToken='string'
)

Но когда я выполняю код, я получаю следующую ошибку:

"botocore.errorfactory.InvalidInputException: при вызове операции ListAccounts произошла ошибка (InvalidInputException): вы указали недопустимое значение для nextToken. Вы должны получить значение из ответа на предыдущий вызов API."

Я действительно озадачен тем, что это значит. Я вижу NextToken, и я могу найти много ссылок на него в документации AWS, но я не могу понять, как на самом деле его использовать. Мол, что мне с этим делать?

8 ответов

Решение

Не воспринимайте примеры boto3 буквально (они не являются реальными примерами). Вот как это работает:

1) При первом звонке list_accounts ты сделаешь это без NextTokenтак просто

getListAccounts = org_client.list_accounts()

2) Это вернет ответ JSON, который выглядит примерно так (это то, что сохраняется в вашем getListAccounts переменная):

{
    "Accounts": [<lots of accounts information>], 
    "NextToken": <some token>
}

Обратите внимание, что NextToken возвращается только в случае, если у вас больше аккаунтов, чем один list_accounts звонок может вернуться, обычно это 100 (в документации по boto3 не указано, сколько по умолчанию). Если все счета были возвращены за один звонок, то нет NextToken в ответ!

3) Таким образом, если и только если не все учетные записи были возвращены при первом вызове, вы теперь хотите вернуть больше учетных записей, и вам придется использовать NextToken чтобы сделать это:

getListAccountsMore = org_client.list_accounts(NextToken=getListAccounts['NextToken'])

4) Повторять пока нет NextToken больше не возвращается в ответе (тогда вы получили все учетные записи).

Именно так AWS SDK обрабатывает нумерацию страниц во многих случаях. Вы увидите использование NextToken в других сервисных клиентах.

То же, что и другой ответ, но с коротким фрагментом с простым while петля.

response = client.list_accounts()
results = response["Accounts"]
while "NextToken" in response:
    response = client.list_accounts(NextToken=response["NextToken"])
    results.extend(response["Accounts"])

Вместо этого вы можете использовать get_paginator api. найдите ниже пример. В моем случае использования мне нужно было получить все значения из хранилища параметров SSM и я хотел сравнить их со строкой.

import boto3
import sys

LBURL = sys.argv[1].strip()
client = boto3.client('ssm')
p = client.get_paginator('describe_parameters')
paginator = p.paginate().build_full_result()
for page in paginator['Parameters']:
    response = client.get_parameter(Name=page['Name'])
    value = response['Parameter']['Value']
    if LBURL in value:
        print("Name is: " + page['Name'] + " and Value is: " + value)

Вот мой пример, где я использовал NextToken, чтобы проверить, существует ли секрет внутри SecretManager. Есть также некоторые отпечатки, которые полезно визуализировать в первый раз.

def check_if_secret_existv2(username):
    results_for_call=5
    response = client.list_secrets(MaxResults=results_for_call)
    i=0
    while True:
        i=i+1
        if 'NextToken' in response:
            response = client.list_secrets(MaxResults=results_for_call,NextToken=response['NextToken'])
        else:
            response = client.list_secrets(MaxResults=results_for_call)

        for secret in response['SecretList']:
            print(secret['Name'])
            if secret['Name'] == username:
                return True
        print('End cycle '+str(i))

        if 'NextToken' not in response:
            break
    return False

print(check_if_secret_existv2(myusername))

Я попытался перечислить имена секретов в моем диспетчере секретов, используя boto3 python:

      secrets = secret_client.list_secrets()
secrets_manager = (secrets['SecretList'])

for secret in secrets_manager: 
    print ("{0}".format(secret['Name']))

Полный список был около 20, но на выходе было только 5 секретных имен.

Обновил код ниже, он сработал:

      secrets = secret_client.list_secrets()
secrets_manager = (secrets['SecretList'])

while "NextToken" in secrets:
    secrets = secret_client.list_secrets(NextToken=secrets["NextToken"])
    secrets_manager.extend(secrets['SecretList'])

for secret in secrets_manager: 
    print ("{0}".format(secret['Name']))

Я знаю, что это другой клиент, но тем, у кого все еще есть проблемы, мне потребовалось некоторое время, чтобы понять:

Этот клиент также требует включения параметров начального запроса. Вот код:

import boto3

lex_client = boto3.client("lex-models")
response = lex_client.get_slot_types(
    nameContains='search_string',
    maxResults=50
)
slot_types = response['slotTypes']
while 'nextToken' in response.keys():
    response = lex_client.get_slot_types(nameContains='search_string', maxResults=50, nextToken=response['nextToken'])
    slot_types.extend(response['slotTypes'])
print('here are the slot types', slot_types)
      resp = dynamodb.execute_statement(Statement="SELECT gateway FROM 
Table_Name")
data = resp['Items']
#print(resp['NextToken'])
while 'NextToken' in resp:
    resp = dynamodb.execute_statement(Statement="SELECT gateway FROM 
    Table_Name", NextToken =resp['NextToken'])
    data.extend(resp['Items'])

И еще один пример возврата списка LogGroupNames с использованием nextToken:

          response = logs_client.describe_log_groups(limit=50)
    results = response["logGroups"]
    log.info(f'    response: {response}')
    log.info(f'    results: {results}')
    while "nextToken" in response:
        log.info('    nextToken')
        response = logs_client.describe_log_groups(nextToken=response["nextToken"], limit=50)
        results.extend(response["logGroups"])

    log_group_name_list = []
    for groups in results:
        log_group_name = groups['logGroupName']
        log_group_name_list.append(log_group_name)
    log.info(f'    log_group_name_list: {log_group_name_list}')
    log.info(f'    log_group_name_list:type: {type(log_group_name_list)}')
Другие вопросы по тегам