Ansible in AWS Lambda
As member of the new PaaS generation, AWS Lambda provides a “serverless” computing platform: the developer no longer has to worry about capacity planning, code deploys, server updates, etc., basically a step closer to the “NoOps” controversial Nirvana.
When python support was added to AWS Lambda, I wondered if it would be possible to use it replicate some of the Ansible Tower functionality: features like running on-demand playbooks or call ansible from cloud-init.
According to AWS documentation, a deployment package is required. The first step to create it is installing ansible into a target directory: lambda, as seen below.
pip install ansible==1.9.4 -t lambda
Now let’s call ansible using the python API:
Zip the contents, so as to create the deployment package:
pushd lambda; \
zip -r ../lambda.zip ../ansible_lambda.py ../test.yml * ; \
popd
Configure a AWS Lambda function:
However, when testing, the following message appears:
So what happened? What did we do wrong? According to the AWS Lambda FAQ:
Q: What if I need scratch space on disk for my AWS Lambda function?A: Each Lambda function receives 500MB of non-persistent disk space in its own /tmp directory.
In light of these instructions, we’ll have to take a step back and perform a tweak in the code:
After packaging and uploading the new code:
{
"localhost": {
"changed": 1,
"failures": 0,
"ok": 2,
"skipped": 0,
"unreachable": 0
}
}
Amazon API Gateway
To leverage the lambda capabilities, API Gateway will work as an entry point with a simple POST http endpoint:
Use the previously created lambda function:
Deploy it:
Test it using curl:
$ curl -X POST -d'{}' https://xxxxxx.execute-api.us-west-2.amazonaws.com/test{
"localhost": {
"unreachable": 0,
"skipped": 0,
"ok": 2,
"changed": 1,
"failures": 0
}
}
Before going any further, be sure to check API Gateway limits:
10-second timeout for both AWS Lambda and HTTP back-end integrations. This limit cannot be changed currently.
A possible workaround to this limitation is Amazon SNS. A high level architecture diagram for this approach:
First create a new SNS topic:
Write a new lambda function that will push new messages from API Gateway into SNS:
The IAM Role for this function:
Change ansible python script to support SNS messages:
Modify ansible lambda function to trigger based on SNS messages:
Update the API Gateway configuration and point to the SNS message pusher function:
Deploy the API and test:
$ curl -X POST -d'{"playbook": "test.yml", "inventory": ["localhost"]}' https://xxxxxx.execute-api.us-west-2.amazonaws.com/test{
"ResponseMetadata": {
"HTTPStatusCode": 200,
"RequestId": “xxx"
},
"MessageId": “xxx"
}
To debug we need to check cloudwatch log:
PLAY [all]**********************************************************
GATHERING FACTS ****************************************************
ok: [localhost]
TASK: [shell echo test] ********************************************
changed: [localhost]
Everything is now working as expected!
Next Steps
I have shown how to run ansible in lambda, however some extra steps will have to be made in order for it to be useful:
- Use paramiko for ssh connections;
- Download playbooks and roles from a S3 bucket;
- Download/Decrypt secrets using KMS;
- Notify, for example, a slack channel for results;
- Workaround the 300 seconds execution limit for lambda functions.