src/providers/ami-root-device.lambda.ts
Rama main · réplica de lectura del repositorio original.
import { DescribeImagesCommand, DescribeLaunchTemplateVersionsCommand, EC2Client } from '@aws-sdk/client-ec2';
import { GetImageCommand, ImagebuilderClient } from '@aws-sdk/client-imagebuilder';
import { GetParameterCommand, SSMClient } from '@aws-sdk/client-ssm';
import * as AWSLambda from 'aws-lambda';
import { customResourceRespond } from '../lambda-helpers';
const ssm = new SSMClient();
const ec2 = new EC2Client();
const ib = new ImagebuilderClient();
async function handleAmi(event: AWSLambda.CloudFormationCustomResourceEvent, ami: string) {
const imageDescs = (await ec2.send(new DescribeImagesCommand({ ImageIds: [ami] })));
if (imageDescs.Images?.length !== 1) {
await customResourceRespond(event, 'FAILED', `${ami} doesn't exist`, 'ERROR', {});
return;
}
const rootDevice = imageDescs.Images[0].RootDeviceName;
if (!rootDevice) {
await customResourceRespond(event, 'FAILED', `${ami} has no root device`, 'ERROR', {});
return;
}
console.log({
notice: 'Resolved AMI root device',
ami,
rootDevice,
});
await customResourceRespond(event, 'SUCCESS', 'OK', rootDevice, {});
return;
}
export async function handler(event: AWSLambda.CloudFormationCustomResourceEvent, context: AWSLambda.Context) {
try {
console.log({
notice: 'CloudFormation custom resource request',
...event,
ResponseURL: '...',
});
const ami = event.ResourceProperties.Ami as string;
switch (event.RequestType) {
case 'Create':
case 'Update':
if (ami.startsWith('ami-')) {
console.log({
notice: 'Checking AMI',
ami,
});
await handleAmi(event, ami);
break;
}
if (ami.startsWith('resolve:ssm:')) {
const ssmParam = ami.substring('resolve:ssm:'.length);
console.log({
notice: 'Checking SSM',
ssmParam,
});
const ssmValue = (await ssm.send(new GetParameterCommand({ Name: ssmParam }))).Parameter?.Value;
if (!ssmValue) {
await customResourceRespond(event, 'FAILED', `${ami} has no value`, 'ERROR', {});
break;
}
await handleAmi(event, ssmValue);
break;
}
if (ami.startsWith('ssm:')) {
const ssmParam = ami.substring('ssm:'.length);
console.log({
notice: 'Checking SSM',
ssmParam,
});
const ssmValue = (await ssm.send(new GetParameterCommand({ Name: ssmParam }))).Parameter?.Value;
if (!ssmValue) {
await customResourceRespond(event, 'FAILED', `${ami} has no value`, 'ERROR', {});
break;
}
await handleAmi(event, ssmValue);
break;
}
if (ami.startsWith('lt-')) {
console.log({
notice: 'Checking Launch Template',
launchTemplateId: ami,
});
const lts = await ec2.send(new DescribeLaunchTemplateVersionsCommand({ LaunchTemplateId: ami, Versions: ['$Latest'] }));
if (lts.LaunchTemplateVersions?.length !== 1) {
await customResourceRespond(event, 'FAILED', `${ami} doesn't exist`, 'ERROR', {});
break;
}
if (!lts.LaunchTemplateVersions[0].LaunchTemplateData?.ImageId) {
await customResourceRespond(event, 'FAILED', `${ami} doesn't have an AMI`, 'ERROR', {});
break;
}
await handleAmi(event, lts.LaunchTemplateVersions[0].LaunchTemplateData.ImageId);
break;
}
if (ami.match('^arn:aws[^:]*:imagebuilder:[^:]+:[^:]+:image/.*$')) {
console.log({
notice: 'Checking Image Builder',
imageBuildVersionArn: ami,
});
const img = await ib.send(new GetImageCommand({ imageBuildVersionArn: ami }));
const actualAmi = img.image?.outputResources?.amis?.[0]?.image;
if (!actualAmi) {
await customResourceRespond(event, 'FAILED', `${ami} doesn't have an AMI`, 'ERROR', {});
break;
}
await handleAmi(event, actualAmi);
break;
}
await customResourceRespond(event, 'FAILED', `Unknown type of AMI ${ami}`, 'ERROR', {});
break;
case 'Delete':
console.log({
notice: 'Nothing to delete',
ami,
});
await customResourceRespond(event, 'SUCCESS', 'OK', event.PhysicalResourceId, {});
break;
}
} catch (e) {
console.error({
notice: 'Failed to resolve AMI root device',
error: e,
});
await customResourceRespond(event, 'FAILED', (e as Error).message || 'Internal Error', context.logStreamName, {});
}
}