使用Boto3创建AWS组织账户未返回AccountId,无法移动至OU
问题:AWS Organizations创建账户后无法获取AccountId执行移动操作
我正在开发一个Lambda函数,功能是接收账户详情后在AWS组织内创建账户,随后将其移动到指定OU。目前账户能创建成功,但因为create_account的响应里没返回AccountId,导致无法执行后续的移动操作。
我的代码
import json import boto3 import string import random def lambda_handler(event, context): client = boto3.client('organizations') root_ou_id = "..." quarantine_ou_id = "..." random_string = get_random_string(5) user_name = random_string user_email = random_string + "@nctest.com" print("user_name: " + user_name) print("user_email: " + user_email) response = client.create_account( Email=user_email, AccountName=user_name, Tags=[ { 'Key': 'account', 'Value': user_name }, { 'Key': 'email', 'Value': user_email }, { 'Key': 'sandbox', 'Value': '' }, { 'Key': 'created_by', 'Value': 'lambda' } ]) print("RESPONSE RECEIVED: " + json.dumps(response, indent=4, sort_keys=True, default=str)) if 'FailureReason' in response['CreateAccountStatus']: return { 'statusCode': 500, 'body': json.dumps(response) } else: account_id = response['CreateAccountStatus']['AccountId'] print(account_id) # Returns nothing at this time. https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/organizations/client/move_account.html client.move_account( AccountId=account_id, SourceParentId=root_ou_id, DestinationParentId=quarantine_ou_id ) return { 'statusCode': 200, 'body': { 'id': account_id, 'account_name': user_name, 'account_email': user_email, 'status': "Account Created" } }
报错信息
[ERROR] KeyError: 'AccountId'
收到的响应内容
{ "CreateAccountStatus":{ "AccountName":"dnpms", "Id":"car-xxxx", "RequestedTimestamp":"2023-06-30 09:58:24.686000+00:00", "State":"IN_PROGRESS" }, "ResponseMetadata":{ "HTTPHeaders":{ "content-length":"151", "content-type":"application/x-amz-json-1.1", "date":"Fri, 30 Jun 2023 09:58:23 GMT", "x-amzn-requestid":"xxxx" }, "HTTPStatusCode":200, "RequestId":"xxxx", "RetryAttempts":0 } }
解决办法
核心原因
create_account是异步操作,调用后立即返回的响应里状态是IN_PROGRESS,此时账户还在创建中,不会返回AccountId;只有当账户创建完成,状态变为SUCCEEDED时,AccountId才会出现在响应里。
具体方案
通过轮询describe_create_account_status接口查询账户创建状态,直到状态变为SUCCEEDED,再提取AccountId执行移动操作。
修改后的代码示例
import json import boto3 import string import random import time def get_random_string(length): letters = string.ascii_lowercase return ''.join(random.choice(letters) for i in range(length)) def lambda_handler(event, context): client = boto3.client('organizations') root_ou_id = "..." quarantine_ou_id = "..." random_string = get_random_string(5) user_name = random_string user_email = random_string + "@nctest.com" print(f"user_name: {user_name}") print(f"user_email: {user_email}") # 发起创建账户请求 create_response = client.create_account( Email=user_email, AccountName=user_name, Tags=[ {'Key': 'account', 'Value': user_name}, {'Key': 'email', 'Value': user_email}, {'Key': 'sandbox', 'Value': ''}, {'Key': 'created_by', 'Value': 'lambda'} ]) create_status_id = create_response['CreateAccountStatus']['Id'] print(f"Create account request ID: {create_status_id}") # 轮询查询创建状态,最多等待5分钟(可根据实际调整) max_retries = 30 retry_interval = 10 # 每次间隔10秒 account_id = None for _ in range(max_retries): status_response = client.describe_create_account_status(CreateAccountRequestId=create_status_id) current_status = status_response['CreateAccountStatus']['State'] if current_status == 'SUCCEEDED': account_id = status_response['CreateAccountStatus']['AccountId'] print(f"Account created successfully, ID: {account_id}") break elif current_status == 'FAILED': failure_reason = status_response['CreateAccountStatus'].get('FailureReason', 'Unknown failure') print(f"Account creation failed: {failure_reason}") return { 'statusCode': 500, 'body': json.dumps({'error': 'Account creation failed', 'reason': failure_reason}) } else: print(f"Account creation in progress, current state: {current_status}") time.sleep(retry_interval) if not account_id: return { 'statusCode': 500, 'body': json.dumps({'error': 'Account creation timed out'}) } # 执行移动账户操作 try: client.move_account( AccountId=account_id, SourceParentId=root_ou_id, DestinationParentId=quarantine_ou_id ) print(f"Account {account_id} moved to OU {quarantine_ou_id} successfully") except Exception as e: print(f"Failed to move account: {str(e)}") return { 'statusCode': 500, 'body': json.dumps({'error': 'Failed to move account', 'reason': str(e)}) } return { 'statusCode': 200, 'body': json.dumps({ 'id': account_id, 'account_name': user_name, 'account_email': user_email, 'status': "Account Created and Moved" }) }
注意事项
- 调整轮询的
max_retries和retry_interval参数,匹配你的账户创建耗时(一般创建AWS账户需要1-3分钟) - Lambda的执行超时时间要设置得比轮询总等待时间长,避免函数提前终止
- 确保Lambda角色拥有
organizations:DescribeCreateAccountStatus和organizations:MoveAccount的权限
内容的提问来源于stack exchange,提问作者user2519653
相关产品推荐
相关产品推荐

