Trick Sam into building your Lambda Layers

Right now, the SAM CLI doesn’t support building Lambda Layers; those magical additions to Lambda that allow you to defined shared dependencies and modules. If you’re unfamiliar, you can read more about them here:

New for AWS Lambda – Use Any Programming Language and Share Common Components

If you read my last article on using sam build, you might think to yourself, “Hey, I can add Layers into my template to share common code across all my Lambdas!”, but hold on! At the moment sam build does not support building Layers the way it builds Lambda packages.

But, there is a hacky way around that. Here’s our repository:

carbon (7)

Now here’s the contents of template.yaml:

carbon (6).png

We’ve defined an AWS::Serverless::Function resource, but with no events, or any other attributes for that matter. We have also defined a AWS::Serverless::LayerVersion resource for our Lambda Layer, but the ContentUri path points to the build directory for the Lambda function.

See where this is going?

sam build will install all the dependencies for our Layer and copy its code into the build directory, and then when we call sam package the Layer will use that output! Spiffy. This does result in an orphan Lambda function that will never be used, but it won’t hurt anything just sitting out there.

Now, we aren’t done quite yet. According to AWS’s documentation, you need to place Python resources within a python directory inside your Layer. The zip file that sam build creates will be extracted into /opt, but the runtimes will only look, by default in a matching directory within /opt (so in the case of Python, that would be /opt/python).

See AWS Lambda Layers documentation for more details.

We can’t tell sam build to do that, but we can still get around this inside our Lambda functions that use the new Layer by adding /opt into sys.path (import searches all of the locations listed here when you call it). Here’s an example Python Lambda function that does this:

carbon (5)

Performing a test execution gives us the following output:

START RequestId: fd5a0bf2-f9af-11e8-bff4-ab8ada75cf17 Version: $LATEST
['/var/task', '/opt/python/lib/python3.6/site-packages', '/opt/python', '/var/runtime', '/var/runtime/awslambda', '/var/lang/lib/python36.zip', '/var/lang/lib/python3.6', '/var/lang/lib/python3.6/lib-dynload', '/var/lang/lib/python3.6/site-packages', '/opt/python/lib/python3.6/site-packages', '/opt/python', '/opt']
<module 'pymysql' from '/opt/pymysql/__init__.py'>
<module 'sqlalchemy' from '/opt/sqlalchemy/__init__.py'>
<module 'stored_procedures' from '/opt/stored_procedures.py'>
END RequestId: fd5a0bf2-f9af-11e8-bff4-ab8ada75cf17
REPORT RequestId: fd5a0bf2-f9af-11e8-bff4-ab8ada75cf17	Duration: 0.82 ms	Billed Duration: 100 ms 	Memory Size: 128 MB	Max Memory Used: 34 MB

Voila! We can see the inclusion of /opt into our path (and the expected path of /opt/python before it) and that our dependencies and custom module were all successfully imported.

It breaks PEP8 a little, but it gets the job done and we have now successfully automated the building and deployment of our Lambda Layer using AWS’s provided tooling.

 

 

Advertisements

Possum is dead; long live the squirrel.

Sam Build

A part of me is sorry to say that the title is not clickbait. Just before re:Invent, the AWS SAM developers made a pretty big announcement:

SAM CLI Introduces sam build Command

You can now use the sam build command to compile deployment packages for AWS Lambda functions written in Python using the AWS Serverless Application Model (AWS SAM) Command Line Interface (CLI).

All you need to do is*:

carbon

This command will iterate over your SAM template and output ready to package versions of your template and Python Lambdas to a .aws-sam/build directory. This lines up exactly with work I was preparing to do for possum, but AWS has gone ahead and done all the work.

* With other required arguments depending on your environment.

In fact, you’ll find that sam build nearly has feature parity with possum with a few exceptions which I’ll go into. Let’s take a look at what one of my serverless projects looks like as an example:

MyApp/
├── src/
|   └── functions/
│       └── MyLambda/
│           ├── my_lambda.py
│           └── requirements.txt
├── Pipfile
├── Pipfile.lock
└── template.yaml

I use pipenv for managing my development environments. The project’s overall dependencies are defined in my Pipfile while the pinned versions of those dependencies are in the Pipfile.lock. Individual dependencies for my Lambdas are defined in their own requirements.txt files within their directories.

I use PyCharm for all of my Python development. Using pipenv to manage the individual virtual environment for a given project allows me to take advantage of the autocompletion features of PyCharm across all the Lambda functions I’m working on. I maintain the individual requirements.txt files for each of my Lambdas and have their listed packages match the version in my Pipfile.lock (I have scripting in possum 1.5.0 that manages syncing the package versions in the requirements.txt files for me).

Now, when I run sam build it will perform all the same actions as possum, but instead of creating the zipped archive and uploading straight to S3 the built Lambdas will be available within the project’s directory.

Possum was originally written as a replacement for sam package that would include dependencies. It would upload the Lambda packages directly to an S3 bucket.

MyApp/
├── .aws-sam/
|   └── build/
|       ├── MyLambda/
│       |   ├── installed_depencency/
│       |   |   └── {dependency files}
│       |   └── my_lambda.py
|       └── template.yaml
├── src/
|   └── functions/
│       └── MyLambda/
│           ├── my_lambda.py
│           └── requirements.txt
├── Pipfile
├── Pipfile.lock
└── template.yaml

The new template located at .aws-sam/build/template.yaml has had the CodeUri keys updated to reference the relative paths within the .aws-sam/build directory. You will see that these copies of the Lambda code now contain all the dependencies that were defined within the requirements.txt file.

The example above generalizes this. Just to show you, the ApiContributorRegistration Lambda for CommunityPatch installs the cryptography and jsonschema packages. This is what the output looks like for a built Lambda:

CommunityPatch/
├── .aws-sam/
    └── build/
        └── ApiContributorRegistration/
            ├── asn1crypto/
            ├── asn1crypto-0.24.0.dist-info/
            ├── cffi/
            ├── cffi-1.11.5.dist-info/
            ├── cryptography/
            ├── cryptography-2.4.1.dist-info/
            ├── idna/
            ├── idna-2.7.dist-info/
            ├── jsonschema/
            ├── jsonschema-2.6.0.dist-info/
            ├── pycparser/
            ├── pycparser-2.19.dist-info/
            ├── schemas/
            ├── six-1.11.0.dist-info/
            ├── _cffi_backend.cpython-36m-x86_64-linux-gnu.so
            ├── api_contributor_registration.py
            ├── requirements.txt
            └── six.py

Dependencies usually have dependencies of their own (those two packages became seven!). And that’s just one Lambda.

Sam Invoke

Now, at this point you could take the output from sam build and perform sam package to get everything loaded into S3 and have a deployment template to run in CloudFormation. However, now that we have a build template we can take advantage of the SAM CLI’s powerful test features which possum was working towards adopting:

carbon (1).png

We can unit test our Lambdas using generated AWS events from the SAM CLI! I’ll cover my workflow for this in more detail at a later time, but before deploying the entire app out to AWS we can now perform some sanity checks that the Lambdas should execute successfully when given a proper payload. Ideally, you would want to generate multiple event payloads to cover a variety of potential invocations.

Sam Package/Deploy

From here the standard package and deploy steps follow (using either the sam or aws CLI tools) which I won’t cover here as I’ve done so in other posts. The full process referencing the new .aws-sam/build directory looks like this:

carbon (4).png

sam package knows to use the template output from sam build without having to specify the path to it!

Gotchas

While all of this is great, let’s cover the exceptions I alluded to earlier.

sam build will perform the build every single time. Even if you don’t make changes between builds it will still rebuild all your functions. This is agonizingly slow. Preventing unneeded builds was one of the first features that went into possum to speed up my personal development. The AWS devs have been listening to some of my feedback on how I implemented this and are looking into adopting a similar solution for sam build.

Every Lambda must have a requirements.txt file even if they don’t have any external dependencies. I ran into this one right away. At the moment, sam build expects there to always be a requirements.txt file within a Lambda function’s code directory. Use a blank file for simple Lambdas as a workaround. The AWS devs are aware of this and will be fixing it.

python must resolve to a Python environment of the same version as your serverless app. If python resolves to a different version (like on a Mac where it resolves to the 2.7 system executable) activate a virtual environment of the correct version as a workaround. You should be able to easily do this if you’re using pipenv by running pipenv shell. The reason this isn’t an issue for possum is because possum relies on pipenv for generating the correct Python build environment based upon the runtime version defined in the template. The AWS devs have been taking my feedback and are looking into this.

Edit: The below wheel issue is fixed in sam 0.8.1!

You may run into The error message “Error: PythonPipBuilder:ResolveDependencies – {pycparser==2.19(sdist)}”. This happens if you’re using a version of Python that didn’t include the wheel package. This will be fixed in a future release, but you can pip install wheel in the Python environment that sam was installed to as a workaround.

You’re also going to run into that error when you try to us the –use-container option because the Docker image pulled for the build environment is also missed that package.

The workaround is to build intermediary image based on lambci/lambda:build-python3.6, install the wheel package, and then tag it using the same tag (yes, you’re tagging an image to override the existing tag with your own custom one) . This will also be fixed in a future release.

Return to form

I failed to live up to the goals in my Blog switch up post over the course of this summer, but that has now finally changed.

Posts return starting next week. I’ll be following the schedule I originally aspired to:

On Monday I’ll have a post up about Jamf The Gathering where I’ll be discussing that trading card bot that made its way into the MacAdmins Slack for JNUC.

Next Friday’s Dev Update will discuss a load of updates that have made (over the past couple weeks) or are making their way into the following open source projects:

  • Possum
  • PatchCLI
  • Patch Server
  • CommunityPatch

Blog switch up

I haven’t been posting to the blog with as much frequency as I used to. Partially, I think this is due to my ideas around larger, more in depth posts that require a lot more time sitting down and crafting.

I’m going to try something different post Penn State MacAdmins. I want to start forcing myself to write more technical content, but in smaller bite-sized pieces that focus on fundamentals rather than all encompassing solutions. Topics that are generic, but provide examples which can be used as building blocks. As these are smaller, and more example driven, I’m going to set a goal to post every week on Monday morning, and maybe Wednesday too if I have enough posts in the pipeline.

On Fridays, I want to start posting “Dev Updates”. I have a number of projects that I have open sourced and am committed to updating for Mac admin community. These projects do have channels in the MacAdmins Slack, and their GitHub repos are linked there, but keeping up for followers would involve a lot of back scrolling to find out what has been discussed. I plan to include in these weekly updates: new issues raised on GitHub, recapping discussions from the Slack channels, and describing any work that has been done during that week on features/bugs. This should not only provide a digestible status updates for those who want them, but help keep me focused.

The ultimate goal here is that I’m writing more again. When it comes to learning and bettering myself professionally, there are two ways I go about it: post about it publicly, or present on it publicly. Both of which force you to cover all your bases in the face of public scrutiny.

We’ll see how this goes.

MacAdmins 2018

Four Years of MacAdmins

Back in February of this year I was able to present at MacAD.UK in London (I attended in 2017; had a blast both times). This marked my eight appearance at a conference as a speaker since joining Jamf in 2012 as the second member of their fledgling IT department. To be fair, four of those appearances were at JNUC. ¯\_(ツ)_/¯

In about month, I’ll be making my fourth appearance, third speaking, at the MacAdmins Conference at Penn State. I have loved this conference every year I’ve attended, and credit is due to the organizers who accumulate a great roster of speakers with a range of content subjects. You’re never without something to listen to.

My first time speaking her, in 2016, I gave what would end up being my most widely viewed presentation to date: Craft Your Own GUIs with Python and Tkinter. The video on YouTube has garnered an insane 82K+ views. I’ll attribute much of that to the subject’s appeal outside of Mac admin circles.

On the second round in 2017 I went a bit further. I attempted, to mixed results, a half day workshop on building Jamf Pro Integrations along with another presentation: How Docker Compose Changed My Life. The workshop had a number of challenges that were all lessons I took to heart for the future: I had drastically underestimated the time needed for my content (we didn’t finish), the notice about prerequisite experience was lost from the sched.com listing, and I had no helpers to assist with questions cause us to pause frequently as I went around the room.


This year I’ll be doing another double feature, but no workshop. Two presentations at the 2018 conference!

Bryson’s doing a Jamf preso?

It’s true. Not counting JNUC, I will be delivering my first official Jamf presentation at a conference. Our gracious Marketing department offered our sponsor slot to me and even allowed me to pick whatever I wanted for the subject!

My choice is something near and dear to me: the recently announced Jamf Marketplace. Why is this near and dear? Creating integrations with Jamf Pro has been a passion of mine, and the Marketplace is a step towards a beautiful future where admins and developers can publish their work for all to share in. I’m very excited for this one.

Session Link: Get Your Tools in Front of Thousands with the Jamf Marketplace

Talking Serverless and AWS

My personal session (not affiliated with Jamf) is all about the new focus in my professional life: serverless application architectures in AWS. That alone can be a pretty broad subject. My presentation will focus on Lambda: the AWS service for running code without servers.

There is a lot of potential for Lambda within your org if you have an AWS account, or would be allowed to create one (you’d be shocked at what you can achieve within the free tier – which I’ll touch on). Beyond the tried and true cron job, you can implement all sorts of crazy even driven workflows with custom processing handled by Lambda functions you’ve written in your preferred language (which is Python, right?).

I’ll be doing a deep dive into subject. We’ll cover the basics of Lambda, how IAM permissions work and how to apply them, the best practices of defining and deploying using CloudFormation (what I call template-first development), and hopefully more if time allows. It’s an area I’ve become very passionate about and I’m looking so forward to being able to present on this to the Mac admin community.

Session Link: Diving into AWS Lambda: An Intro to Serverless for Admins


I hope to see you next month! If you don’t find me wandering the halls between sessions, please reach out on Slack, or peek into Legends. It’s a favorite.

If you’re interested in the presentations I’ve done over the years at various conferences, you can find that list with YouTube links here.