58

Can I write a bash script inside a Lambda function? I read in the aws docs that it can execute code written in Python, NodeJS and Java 8.

It is mentioned in some documents that it might be possible to use Bash but there is no concrete evidence supporting it or any example

Hardik Kamdar
  • 2,561
  • 4
  • 16
  • 21

8 Answers8

27

AWS recently announced the "Lambda Runtime API and Lambda Layers", two new features that enable developers to build custom runtimes. So, it's now possibile to directly run even bash scripts in Lambda without hacks.

As this is a very new feature (November 2018), there isn't much material yet around and some manual work still needs to be done, but you can have a look at this Github repo for an example to start with (disclaimer: I didn't test it). Below a sample handler in bash:

function handler () {
  EVENT_DATA=$1
  echo "$EVENT_DATA" 1>&2;
  RESPONSE="{\"statusCode\": 200, \"body\": \"Hello World\"}"
  echo $RESPONSE
}

This actually opens up the possibility to run any programming language within a Lambda. Here it is an AWS tutorial about publishing custom Lambda runtimes.

mturatti
  • 651
  • 5
  • 10
22

Something that might help, I'm using Node to call the bash script. I uploaded the script and the nodejs file in a zip to lambda, using the following code as the handler.

exports.myHandler = function(event, context, callback) {
  const execFile = require('child_process').execFile;
  execFile('./test.sh', (error, stdout, stderr) => {
    if (error) {
      callback(error);
    }
    callback(null, stdout);
  });
}

You can use the callback to return the data you need.

Rvy Pandey
  • 1,654
  • 3
  • 16
  • 22
Daniel Cortés
  • 486
  • 7
  • 13
17

AWS supports custom runtimes now based on this announcement here. I already tested bash script and it worked. All you need is to create a new lambda and choose runtime of type Custom it will create the following file structure:

mylambda_func
 |- bootstrap 
 |- function.sh 

Example Bootstrap:

#!/bin/sh

set -euo pipefail

# Handler format: <script_name>.<function_name>
# The script file <script_name>.sh  must be located in
# the same directory as the bootstrap executable.
source $(dirname "$0")/"$(echo $_HANDLER | cut -d. -f1).sh"

while true
do
    # Request the next event from the Lambda Runtime
    HEADERS="$(mktemp)"
    EVENT_DATA=$(curl  -v -sS  -LD "$HEADERS"  -X GET  "http://${AWS_LAMBDA_RUNTIME_API}/2018-06-01/runtime/invocation/next")
    INVOCATION_ID=$(grep -Fi Lambda-Runtime-Aws-Request-Id "$HEADERS" | tr -d '[:space:]' | cut -d: -f2)

    # Execute the handler function from the script
    RESPONSE=$($(echo "$_HANDLER" | cut -d. -f2) "$EVENT_DATA")

    # Send the response to Lambda Runtime
    curl  -v  -sS  -X POST  "http://${AWS_LAMBDA_RUNTIME_API}/2018-06-01/runtime/invocation/$INVOCATION_ID/response"  -d "$RESPONSE"
done

Example handler.sh:

function handler () {
    EVENT_DATA=$1

    RESPONSE="{\"statusCode\": 200, \"body\": \"Hello from Lambda!\"}"
    echo $RESPONSE
}

P.S. However in some cases you can't achieve what's needed because of the environment restrictions, such cases need AWS Systems Manager to Run command, OpsWork (Chef/Puppet) based on what you're more familiar with or periodically using ScheduledTasks in ECS cluster.

More Information about bash and how to zip and publish it, please check the following links:

Muhammad Soliman
  • 21,644
  • 6
  • 109
  • 75
15

As you mentioned, AWS does not provide a way to write Lambda function using Bash.

To work around it, if you really need bash function, you can "wrap" your bash script within any languages.

Here is an example with Java:

Process proc = Runtime.getRuntime().exec("./your_script.sh");  

Depending on your business needs, you should consider using native languages(Python, NodeJS, Java) to avoid performance loss.

Thomas L.
  • 1,294
  • 9
  • 13
  • Is there a similar command like this for .NET? The only way I can find seems to depend on "/bin/bash", but your method seems to skip that by invoking through the environment. – I. Buchan Apr 19 '18 at 18:19
11

I just was able to capture a shell command uname output using Amazon Lambda - Python.

Below is the code base.

from __future__ import print_function

import json
import commands

print('Loading function')

def lambda_handler(event, context):
    print(commands.getstatusoutput('uname -a'))

It displayed the output

START RequestId: 2eb685d3-b74d-11e5-b32f-e9369236c8c6 Version: $LATEST
(0, 'Linux ip-10-0-73-222 3.14.48-33.39.amzn1.x86_64 #1 SMP Tue Jul 14 23:43:07 UTC 2015 x86_64 x86_64 x86_64 GNU/Linux')
END RequestId: 2eb685d3-b45d-98e5-b32f-e9369236c8c6
REPORT RequestId: 2eb685d3-b74d-11e5-b31f-e9369236c8c6  Duration: 298.59 ms Billed Duration: 300 ms     Memory Size: 128 MB Max Memory Used: 9 MB   

For More information check the link - https://aws.amazon.com/blogs/compute/running-executables-in-aws-lambda/

Naveen Vijay
  • 15,928
  • 7
  • 71
  • 92
5

Its possible using the 'child_process' node module.

const exec = require('child_process').exec;

exec('echo $PWD && ls', (error, stdout, stderr) => {
  if (error) {
    console.log("Error occurs");
    console.error(error);
    return;
  }
  console.log(stdout);
  console.log(stderr);
});

This will display the current working directory and list the files.

Kishor Unnikrishnan
  • 1,928
  • 4
  • 21
  • 33
  • You need to wrap this in Promise in order to use this with async-await syntax but overall it works! – iaforek Apr 03 '20 at 11:30
1

As others have pointed out, in Node.js you can use the child_process module, which is built-in to Node.js. Here's a complete working sample:

app.js:

'use strict'
const childproc = require('child_process')

module.exports.handler = (event, context) => {
    return new Promise ((resolve, reject) => {
        const commandStr = "./script.sh"
        const options = {
            maxBuffer: 10000000,
            env: process.env
        }
        childproc.exec(commandStr, options, (err, stdout, stderr) => {
            if (err) {
                console.log("ERROR:", err)
                return reject(err)
            }
            console.log("output:\n", stdout)
            const response = {
                statusCode: 200,
                body: {
                    output: stdout
                }
            }
            resolve(response)
        })
    })
}

script.sh:

#!/bin/bash

echo $PWD
ls -l

response.body.output is

/var/task
total 16
-rw-r--r-- 1 root root 751 Oct 26  1985 app.js
-rwxr-xr-x 1 root root  29 Oct 26  1985 script.sh

(NOTE: I ran this in an actual Lambda container, and it really does show the year as 1985).

Obviously, you can put whatever shell commands you want into script.sh as long as it's included in the Lambda pre-built container. You can also build your own custom Lambda container if you need a command that's not in the pre-built container.

Patrick Chu
  • 1,513
  • 14
  • 15
0

Now you can create Lambda functions written in any kind of language by providing a custom runtime which teaches the Lambda function to understand the syntax of the language you want to use.

You can follow this to learn more AWS Lambda runtimes

Shajibur Rahman
  • 436
  • 3
  • 12