visit
Ever wish you could combine the portability of containers, with the scalability of Lambda functions? Well, now you can! Recently, AWS released a new way for developers to package and deploy their Lambda functions as “Container Images”. This enables us to build a Lambda with a docker image of your own creation. The benefit of this is we can now easily include dependencies along with our code in a way that is more familiar to developers. If you have used docker containers before, then this is much simpler to get started with than the other option - Lambda layers.
AWS has provided developers with a number of base images for each of the current Lambda runtimes (Python, Node.js, Java, .NET, Go, Ruby). It is easy for a developer to then use one of these images as a base and build their own image on top.Of course, there are many sensible use cases for container images. Perhaps you want to include some machine learning dependencies? Maybe you would love to have FFMPEG in your Lambda for your video processing needs? Or you want to nuke your entire AWS account to avoid a hefty bill?In this article, we are going to build a container image with installed! This will delete everything in an AWS account (excluding our fancy new container image Lambda). Nuke is built using Go but we are going to get started with the and build our own Lambda using JavaScript. This library isn’t available on NPM, so there is no easy way to pull it into our Lambda function, but container images provide a way for developers to mix and match different tools to build a scalable solution to the problem they are trying to solve.
To get started with our new container image, we can create a Dockerfile like so:FROM public.ecr.aws/lambda/nodejs:12
COPY ./lambda/nuke.js ./lambda/package*.json ./
RUN npm install
CMD [ "nuke.lambdaHandler" ]
As you can see, we are building from the
lambda/nodejs:12
base image, and copying over our Lambda function code. Notice the last line of our Dockerfile, CMD [ "nuke.lambdaHandler" ]
. Because we are using one of the base images, it comes pre-installed with the .The runtime interface client in your container image manages the interaction between Lambda and your function code. The Runtime API, along with the Extensions API, defines a simple HTTP interface for runtimes to receive invocation events from Lambda and respond with success or failure indications.
Therefore
CMD [ "nuke.lambdaHandler" ]
lets the interface client know what handler function to call when it receives an invocation event.Before we add the nuclear option, let's create the skeleton for our handler function:exports.lambdaHandler = async (event) => {
const response = { statusCode: 200 };
return response;
};
.
├── Dockerfile
├── docker-compose.yml
└── lambda
├── nuke.js
└── package.json
docker build -f ./Dockerfile -t instil-nuke .
And to run it locally:docker run -p 9000:8080 instil-nuke
Then to test our function locally, we just need to hit our lambda with an http request. In this example we are posting an empty JSON body:➜ curl -XPOST "//localhost:9000/2015-03-31/functions/function/invocations" -d '{}'
{"statusCode":200}%
The URL seems strange, but the Runtime Interface Emulator is simply providing an endpoint that matches the . The only difference between this local URL and the real API URL is that our function name is hardcoded as a function.
Being able to run our function locally like this greatly reduces the feedback loop when developing your Lambda. There are other options out there for running Lambdas locally; for example, , but the container image approach gives you a local test environment that is much closer to how it will be run on AWS.
Now that we have our project structure in place, let's take a look at adding AWS-nuke to our container image.
FROM public.ecr.aws/lambda/nodejs:12
LABEL maintainer="Instil <[email protected]>"
RUN yum -y update
RUN yum -y install tar gzip
COPY ./resources/aws-nuke-v2.15.0.rc.3-linux-amd64.tar.gz ./resources/nuke-config.yml ./
RUN tar -xzf ./aws-nuke-v2.15.0.rc.3-linux-amd64.tar.gz && mv aws-nuke-v2.15.0.rc.3-linux-amd64 aws-nuke
COPY ./lambda/nuke.js ./lambda/package*.json ./
RUN npm install
CMD [ "nuke.lambdaHandler" ]
const { execSync } = require('child_process');
function run(command) {
console.log(command);
const result = execSync(command, {stdio: 'inherit'});
if (result) {
console.log(result.toString());
}
}
function nuke() {
console.log("Nuking this AWS account...");
const accessKey = process.env.AWS_ACCESS_KEY_ID;
const secretAccessKey = process.env.AWS_SECRET_ACCESS_KEY;
const sessionToken = process.env.AWS_SESSION_TOKEN;
run(`./aws-nuke -c nuke-config.yml --access-key-id ${accessKey} --secret-access-key ${secretAccessKey} --session-token ${sessionToken} --force --force-sleep 3`);
console.log("Your AWS account has been nuked, you can sleep peacefully knowing that you will no longer get an unexpected bill.");
}
exports.lambdaHandler = async (event) => {
nuke();
const response = { statusCode: 200 };
return response;
};
We can use
execSync
to execute a command in our running lambda, it’s easy to see how simple it is to utilize external dependencies in our Lambda environment with this new container image option. Notice that we are pulling AWS access tokens from environment variables so that nuke can use them, this is the for Lambda functions and they are the access keys obtained from the function’s execution role.With our updated container image ready to nuke our account, all we need to do is deploy it. For this we need to create an ECR repository and push our image to it:# Replace [AWS_ACCOUNT_NUMBER] with your own AWS account number
aws ecr create-repository --repository-name instil-nuke --image-scanning-configuration scanOnPush=true
docker tag instil-nuke:latest [AWS_ACCOUNT_NUMBER].dkr.ecr.eu-west-1.amazonaws.com/instil-nuke:latest
aws ecr get-login-password | docker login --username AWS --password-stdin [AWS_ACCOUNT_NUMBER].dkr.ecr.eu-west-1.amazonaws.com
docker push [AWS_ACCOUNT_NUMBER].dkr.ecr.eu-west-1.amazonaws.com/instil-nuke:latest
And that’s it! All that’s left to do is trigger our Lambda function. For our example, we could detonate the nuke once we get a billing alarm over a certain threshold. For the sake of keeping this article focused on container images, let's just trigger it with a test event for now and inspect the output. We will publish another article in the future explaining how to hook this up to a billing alarm.
The above resources would be deleted with the supplied configuration. Provide --no-dry-run to actually destroy resources.
You didn’t think I was actually going to nuke my AWS account, did you? 😊
If you would like to speak to us about how we can help your business either with serverless training or software development, please .This article was originally posted .