事實證明,這個AMI的後代確實來自亞馬遜市場。
烘烤管道的簡化版本是:
帳戶A:
市場AMI(AMI-XXXXXXXX) - >封隔器的構建(AMI-YYYYYYYY) - >共享B帳戶
B帳戶:
然後我發出:
aws ec2 copy-image --encrypted --source-image ami-yyyyyyyy
,並重新可察覺:
An error occurred (InvalidRequest) when calling the CopyImage operation: Images with EC2 BillingProduct codes cannot be copied to another AWS account.
從帳戶BI可以檢查所有者,即與我分享它的帳戶:
$ aws ec2 describe-images --image-id ami-yyyyyyyy --region ap-southeast-2 \
> --query 'Images[0].OwnerId'
這回戶口A
的16位數的帳戶ID在這一點有助於理解,使用Packer烘烤AMI會導致產品代碼顯示爲顯然丟失。但是,這些產品代碼仍然存在,亞馬遜支持可以看到它們。它需要致電亞馬遜支持部門確認此部分。
要解決這一點,我們正在使用這個Python腳本,這似乎令人難以置信的複雜,但至少它的工作:
#!/usr/bin/env python
import argparse
import boto3
import os
import random
import sys
import time
def copy_ec2_image(**kwargs):
client = get_ec2_client()
if os.environ.get('DATE_TIME'):
kwargs['name'] += "-" + os.environ['DATE_TIME']
if kwargs['encrypted']:
kwargs['name'] = "encrypted-" + kwargs['name']
sys.stdout.write("Creating the AMI: %s\n" %kwargs['name'])
sys.stdout.flush()
response = client.copy_image(DryRun=False, SourceRegion=kwargs['source_region'], SourceImageId=kwargs['source_ami_id'], Name=kwargs['name'], Description=kwargs['description'], Encrypted=kwargs['encrypted'], KmsKeyId=kwargs['kms_key_id'])
sys.stdout.write("AMI: %s\n" %response['ImageId'])
sys.stdout.flush()
return response['ImageId'], kwargs
def create_ec2_image(instance_id, **kwargs):
client = get_ec2_client()
if os.environ.get('DATE_TIME'):
kwargs['name'] += "-" + os.environ['DATE_TIME']
kwargs['name'] = "unencrypted-" + kwargs['name']
sys.stdout.write("Creating the AMI: %s\n" %kwargs['name'])
sys.stdout.flush()
response = client.create_image(DryRun=False, InstanceId = instance_id, Name=kwargs['name'])
return response['ImageId'], kwargs
def deregister_ec2_image(ami_id):
client = get_ec2_client()
sys.stdout.write("Deregistering the AMI: %s\n" %ami_id)
sys.stdout.flush()
return client.deregister_image(DryRun=False, ImageId=ami_id)
def get_args():
parser = argparse.ArgumentParser()
parser.add_argument('--source-region', action='store', required=False, metavar='SOURCE_AMI_REGION', dest='source_region', default='ap-southeast-2', help='The name of the region that contains the AMI to copy.')
parser.add_argument('--description', action='store', required=False, metavar='DESCRIPTION', dest='description', default='', help=' A description for the new AMI in the destination region.')
parser.add_argument('--encrypted', required=False, action='store_true', help='Specifies whether the destination snapshots of the copied image should be encrypted.')
parser.add_argument('--kms-key-id', action='store', required=False, metavar='KMS_KEY_ID', dest='kms_key_id', default='', help='The full ARN of the AWS Key Management Service (AWS KMS) CMK to use when encrypting the snapshots of an image during a copy operation. This parameter is only required if you want to use a non-default CMK; if this parameter is not specified, the default CMK for EBS is used.')
parser.add_argument('--source-image-id', action='store', required=True, metavar='SOURCE_AMI_ID', dest='source_ami_id', help='The ID of the AMI to copy.')
parser.add_argument('name', action='store', help='The name of the new AMI in the destination region.')
return parser.parse_args()
def get_account_id():
try:
# We're running in an ec2 instance, get the account id from the
# instance profile ARN
return json.loads(urllib2.urlopen('http://169.254.169.254/latest/meta-data/iam/info/', None, 1).read())['InstanceProfileArn'].split(':')[4]
except:
pass
try:
# We're not on an ec2 instance but have api keys, get the account
# id from the user ARN
return boto3.client('iam').get_user()['User']['Arn'].split(':')[4]
except:
pass
return False
def get_ec2_client():
return boto3.client('ec2')
def get_ec2_image_account_id(ami_id):
client = get_ec2_client()
response = client.describe_images(DryRun=False, ImageIds=[ami_id])
return response['Images'][0]['ImageLocation'].split('/')[0]
def get_ec2_image_status(ami_id, **kwargs):
client = get_ec2_client()
sys.stdout.write("Waiting for AMI to become ready...\n")
sys.stdout.flush()
while True:
response = client.describe_images(DryRun=False, ImageIds=[ami_id])
if response['Images'][0]['State'] == 'available':
sys.stdout.write("AMI successfully created: %s\n" %ami_id)
sys.stdout.flush()
if os.environ.get('JOB_NAME'):
filename = os.environ['JOB_NAME'] + "_ID.txt"
else:
filename = kwargs['name'].title() + "_AMI_ID.txt"
fd = open(filename, 'w')
fd.write(ami_id + "\n")
fd.close()
return 0
sys.stdout.write("state: %s\n" %response['Images'][0]['State'])
sys.stdout.flush()
time.sleep(10)
def get_ec2_instance_status(instance_id, status):
client = get_ec2_client()
if status == 'running':
sys.stdout.write("Waiting for instance (%s) to become ready...\n" %instance_id)
sys.stdout.flush()
while True:
response = client.describe_instances(DryRun=False, InstanceIds=[ instance_id ])
try:
if response['Reservations'][0]['Instances'][0]['State']['Name'] == status:
break
time.sleep(1)
except:
pass
sys.stdout.write("Waiting for instance (%s) to become healthy...\n" %instance_id)
sys.stdout.flush()
while True:
response = client.describe_instance_status(DryRun=False, InstanceIds=[ instance_id ])
try:
if response['InstanceStatuses'][0]['SystemStatus']['Status'] == response['InstanceStatuses'][0]['InstanceStatus']['Status'] == 'ok':
break
time.sleep(1)
except:
pass
sys.stdout.write("Instance up and running!!\n")
sys.stdout.flush()
return
elif status == 'stopped':
sys.stdout.write("Waiting for instance (%s) to stop...\n" %instance_id)
sys.stdout.flush()
while True:
response = client.describe_instances(DryRun=False, InstanceIds=[ instance_id ])
try:
if response['Reservations'][0]['Instances'][0]['State']['Name'] == status:
break
time.sleep(1)
except:
pass
return
elif status == 'terminated':
sys.stdout.write("Waiting for instance (%s) to terminate...\n" %instance_id)
sys.stdout.flush()
while True:
response = client.describe_instances(DryRun=False, InstanceIds=[ instance_id ])
try:
if response['Reservations'][0]['Instances'][0]['State']['Name'] == status:
break
time.sleep(1)
except:
pass
return
def get_ec2_subnet_ids(tag_name):
client = get_ec2_client()
response = client.describe_subnets(DryRun=False, Filters=[ { 'Name': 'tag:Name', 'Values': [ tag_name ] } ])
return [subnet['SubnetId'] for subnet in response['Subnets']]
def launch_ec2_instance(**kwargs):
client = get_ec2_client()
sys.stdout.write("Launching a source AWS instance...\n")
sys.stdout.flush()
if os.environ.get('AWS_BACKEND_SUBNET_IDS'):
subnet_id = random.choice(os.environ.get('AWS_BACKEND_SUBNET_IDS').split(','))
else:
subnet_id = random.choice(get_ec2_subnet_ids('*-BackEnd-*'))
response = client.run_instances(DryRun=False, ImageId=kwargs['source_ami_id'], InstanceType='c4.2xlarge', MinCount=1, MaxCount=1, SubnetId=subnet_id)
instance_id = response['Instances'][0]['InstanceId']
sys.stdout.write("Instance ID: %s\n" %instance_id)
get_ec2_instance_status(instance_id, 'running')
return instance_id
def stop_ec2_instance(instance_id):
client = get_ec2_client()
sys.stdout.write("Stopping the source AWS instance...\n")
sys.stdout.flush()
response = client.stop_instances(DryRun=False, InstanceIds=[ instance_id ])
get_ec2_instance_status(instance_id, 'stopped')
def terminate_ec2_instance(instance_id):
client = get_ec2_client()
sys.stdout.write("Terminating the source AWS instance...\n")
sys.stdout.flush()
response = client.terminate_instances(DryRun=False, InstanceIds=[ instance_id ])
get_ec2_instance_status(instance_id, 'terminated')
def main():
args = get_args()
try:
if get_account_id() == get_ec2_image_account_id(vars(args)['source_ami_id']):
ami_id, kwargs = copy_ec2_image(**vars(args))
get_ec2_image_status(ami_id, **kwargs)
else:
instance_id = launch_ec2_instance(**vars(args))
stop_ec2_instance(instance_id)
unencrypted_ami_id, kwargs = create_ec2_image(instance_id, **vars(args))
get_ec2_image_status(unencrypted_ami_id, **kwargs)
terminate_ec2_instance(instance_id)
vars(args)['source_ami_id'] = unencrypted_ami_id
encrypted_ami_id, kwargs = copy_ec2_image(**vars(args))
get_ec2_image_status(encrypted_ami_id, **kwargs)
deregister_ec2_image(unencrypted_ami_id)
except KeyboardInterrupt:
sys.exit("User aborted script!")
if __name__ == '__main__':
main()
從市場實例的AMI產生的情況下,或者是在任何與Marketplace實例關聯的方式?另請參閱https://serverfault.com/questions/775946/aws-encrypted-ebs-boot-volumes-for-windows-instances和http://blog.open-tribute。org/2017/04/18/ami-copy-failed-billingproduct/ –
據我所知,除非我被誤導,否則不會。我想排除這一點,我需要自己完成所有上游的AMI烘焙,或者有沒有一種測試方法? –
畢竟答案是「是」。下面的完整解釋。 –