Как вы используете "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)}')