I am trying to export a VM AMI to use it in vCenter.
When I run the AWS CLI command as an admin everything works, but when I try to automate the process using a Lambda function it does not.
If I attach an admin role to the function the VM export works, but if I use the roles and permission from the vmexport documents it does not work.
Here my permissions
"Version": "2012-10-17",
"Statement": [
{
"Sid": "VisualEditor0",
"Effect": "Allow",
"Action": [
"ec2:CopySnapshot",
"kms:Decrypt",
"ec2:RegisterImage",
"kms:ReEncrypt*",
"kms:GenerateDataKey*",
"license-manager:ListLicenseSpecificationsForResource",
"ec2:Describe*",
"license-manager:UpdateLicenseSpecificationsForResource",
"ec2:ModifySnapshotAttribute",
"kms:Encrypt",
"kms:DescribeKey",
"license-manager:GetLicenseConfiguration",
"kms:CreateGrant"
],
"Resource": "*"
},
{
"Sid": "VisualEditor1",
"Effect": "Allow",
"Action": [
"s3:GetObject",
"s3:ListBucket",
"s3:GetBucketLocation"
],
"Resource": [
"arn:aws:s3:::mys3bucket/*",
"arn:aws:s3:::mys3bucket"
]
},
{
"Sid": "VisualEditor2",
"Effect": "Allow",
"Action": [
"s3:PutObject",
"s3:GetObject",
"s3:ListBucket",
"s3:GetBucketAcl",
"s3:GetBucketLocation"
],
"Resource": [
"arn:aws:s3:::mys3bucket/*",
"arn:aws:s3:::mys3bucket"
]
}
]
}
trust relationships
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Service": "lambda.amazonaws.com"
},
"Action": "sts:AssumeRole"
},
{
"Sid": "",
"Effect": "Allow",
"Principal": {
"Service": "vmie.amazonaws.com"
},
"Action": "sts:AssumeRole",
"Condition": {
"StringEquals": {
"sts:ExternalId": "vmimport"
}
}
}
]
}
Here my lambda function
import json
import boto3
aws_region = "us-west-1"
client = boto3.client('ec2', region_name=aws_region)
def lambda_handler(event, context):
AMI_ID = event['AMI_ID']
S3BUCKET = event['S3BUCKET']
S3PREFIX = event['S3PREFIX']
DRY_RUN_BOOL = bool(event['DRY_RUN_BOOL'])
response = client.export_image(
DiskImageFormat='VMDK',
DryRun=DRY_RUN_BOOL,
ImageId=AMI_ID,
S3ExportLocation={
'S3Bucket': S3BUCKET,
'S3Prefix': S3PREFIX
},
RoleName='ROL_Lambda_VMImport',
TagSpecifications=[
{
'ResourceType': 'export-image-task',
'Tags': [
{
'Key': 'ami_id',
'Value': AMI_ID
},
{
'Key': 'stable',
'Value': 'false'
},
{
'Key': 'status',
'Value': 'not tested yet'
},
]
},
]
)
From a technical point of view, everything seems to be ok, but I'm always getting an UnauthorizedOperation error without that much description available.
Note that that role has been added to the kms keys used to encrypt the S3 bucket.
CodePudding user response:
For anyone in need, the solution is fairly simple.
Follow the documentation to create a vmimport role.
In the lambda function, under configuration -> Permission do not use vmimport, use the role you created for the function itself.
vmimport will need to be specified in the code, under RoleName.
The function will then run and use the vmimport role to run the specific command only.
import json
import boto3
aws_region = "us-west-1"
client = boto3.client('ec2', region_name=aws_region)
def lambda_handler(event, context):
AMI_ID = event['AMI_ID']
S3BUCKET = event['S3BUCKET']
S3PREFIX = event['S3PREFIX']
DRY_RUN_BOOL = bool(event['DRY_RUN_BOOL'])
response = client.export_image(
DiskImageFormat='VMDK',
DryRun=DRY_RUN_BOOL,
ImageId=AMI_ID,
S3ExportLocation={
'S3Bucket': S3BUCKET,
'S3Prefix': S3PREFIX
},
RoleName='vmimport',
TagSpecifications=[
{
'ResourceType': 'export-image-task',
'Tags': [
{
'Key': 'ami_id',
'Value': AMI_ID
},
{
'Key': 'stable',
'Value': 'false'
},
{
'Key': 'status',
'Value': 'not tested yet'
},
]
},
]
)
The trust relationship assigned to the role under configuration -> Permission should be this :
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Service": "lambda.amazonaws.com"
},
"Action": "sts:AssumeRole"
},
]
}
The Function Role policy should be something like this
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "VisualEditor0",
"Effect": "Allow",
"Action": [
"ec2:ExportImage",
"ec2:CopySnapshot",
"ec2:Describe*",
"license-manager:UpdateLicenseSpecificationsForResource",
"ec2:ModifySnapshotAttribute",
"ec2:CreateTags",
"ec2:RegisterImage",
"license-manager:GetLicenseConfiguration",
"license-manager:ListLicenseSpecificationsForResource"
],
"Resource": "*"
},
{
"Sid": "VisualEditor1",
"Effect": "Allow",
"Action": [
"s3:GetObject",
"s3:ListBucket",
"s3:GetBucketLocation"
],
"Resource": [
"arn:aws:s3:::ami-export/*",
"arn:was:s3:::ami-export"
]
},
{
"Sid": "VisualEditor2",
"Effect": "Allow",
"Action": [
"s3:PutObject",
"s3:GetObject",
"s3:ListBucket",
"s3:GetBucketAcl",
"s3:GetBucketLocation"
],
"Resource": [
"arn:aws:s3:::ami-export/*",
"arn:was:s3:::ami-export"
]
}
]
}
For the role, add also the AWS-managed policy called ReadOnlyAccess. For additional troubleshooting, the policy CloudWatchLogsFullAccess should also be used, to gain visibility into the function logs.