Skip to content

Lambda Inline Code - Using cryptography Library

2

I have a CloudFormation custom resource that needs to generate X.509 certificates (for ACM PCA or Client VPN). I'm using inline Python code (ZipFile in the template) because I want the stack to be self-contained without S3 buckets or layers.

When the Lambda runs, I get:

[ERROR] Runtime.ImportModuleError: Unable to import module 'index': No module named 'cryptography'

My code starts with:

import json
import cfnresponse
from cryptography import x509
from cryptography.x509.oid import NameOID
from cryptography.hazmat.primitives import hashes, serialization
from cryptography.hazmat.primitives.asymmetric import rsa

I know cryptography isn't in the Lambda runtime by default. But I can't use layers (trying to keep the template portable) and I can't use a deployment package with ZipFile. Is there any way to make this work with inline code?

Runtime: Python 3.12, Architecture: x86_64

2 Answers
4
Accepted Answer

You can make this work with inline Lambda code by installing the package at runtime to /tmp and deferring the import. The /tmp directory in Lambda provides 512MB (or up to 10GB if configured) of ephemeral storage, and you can use pip to install packages there during cold starts.

The Pattern: Runtime pip install with deferred import

import json
import subprocess
import sys
import cfnresponse

def install_packages():
    """Install cryptography to /tmp on cold start."""
    subprocess.check_call([
        sys.executable, '-m', 'pip', 'install',
        'cryptography', '--target', '/tmp/pip_packages',
        '--quiet', '--no-cache-dir'
    ])
    sys.path.insert(0, '/tmp/pip_packages')

# Install on cold start (outside handler)
install_packages()

# NOW import cryptography (after install)
from cryptography import x509
from cryptography.x509.oid import NameOID
from cryptography.hazmat.primitives import hashes, serialization
from cryptography.hazmat.primitives.asymmetric import rsa
import datetime


def handler(event, context):
    try:
        if event['RequestType'] == 'Delete':
            cfnresponse.send(event, context, cfnresponse.SUCCESS, {})
            return

        # Generate CA private key
        ca_key = rsa.generate_private_key(
            public_exponent=65537,
            key_size=2048
        )

        # Generate CA certificate
        ca_name = x509.Name([
            x509.NameAttribute(NameOID.COMMON_NAME, "My Custom CA"),
            x509.NameAttribute(NameOID.ORGANIZATION_NAME, "MyOrg"),
        ])

        ca_cert = (
            x509.CertificateBuilder()
            .subject_name(ca_name)
            .issuer_name(ca_name)
            .public_key(ca_key.public_key())
            .serial_number(x509.random_serial_number())
            .not_valid_before(datetime.datetime.utcnow())
            .not_valid_after(datetime.datetime.utcnow() + datetime.timedelta(days=3650))
            .add_extension(x509.BasicConstraints(ca=True, path_length=None), critical=True)
            .sign(ca_key, hashes.SHA256())
        )

        # Serialize to PEM
        ca_key_pem = ca_key.private_bytes(
            encoding=serialization.Encoding.PEM,
            format=serialization.PrivateFormat.TraditionalOpenSSL,
            encryption_algorithm=serialization.NoEncryption()
        ).decode('utf-8')

        ca_cert_pem = ca_cert.public_bytes(
            serialization.Encoding.PEM
        ).decode('utf-8')

        cfnresponse.send(event, context, cfnresponse.SUCCESS, {
            'CACertificatePem': ca_cert_pem,
            'CAPrivateKeyPem': ca_key_pem
        })

    except Exception as e:
        print(f"Error: {str(e)}")
        cfnresponse.send(event, context, cfnresponse.FAILED, {
            'Error': str(e)
        })

Important considerations:

  1. Cold start penalty: The pip install adds 5-15 seconds to cold starts. Set your Lambda timeout to at least 120 seconds (custom resources allow up to 3600s).

  2. Memory: Set memory to at least 512MB — the cryptography library compilation and loading is memory-intensive.

  3. Idempotency: The install runs on every cold start but is fast on warm invocations since the module is already in sys.path. You can add a check: if not os.path.exists('/tmp/pip_packages/cryptography').

  4. Network access: The Lambda needs internet access (NAT Gateway or public subnet) OR a VPC endpoint for PyPI. If your Lambda is in a VPC without internet, this won't work — use a Lambda Layer instead.

  5. Pinning versions: For production, pin the version: 'cryptography==42.0.0' to avoid unexpected breaking changes.

References:

answered 2 months ago

EXPERT

reviewed 2 months ago

EXPERT

reviewed 2 months ago

AWS
SUPPORT ENGINEER

revised 2 months ago

0

The short answer is: no, not with ZipFile inline Lambda code alone.

The cryptography package contains compiled native extensions (_rust, OpenSSL bindings, C/Rust components). AWS Lambda inline code only supports pure Python standard library modules already included in the runtime.

Because of that:

from cryptography import x509

fails with:

Runtime.ImportModuleError: No module named 'cryptography'

Why this happens

When you use:

Code:
  ZipFile: |
    ...

CloudFormation creates a tiny deployment package containing only your inline source code.

There is:

  • no pip install
  • no dependency resolution
  • no compiled wheel support
  • no vendored libraries

So only built-in runtime modules work.


Your realistic options

OptionWorks with ZipFilePortableRecommended
Pure stdlib only❌ (no X.509 support)
Inline + custom minimal crypto implementationTechnicallyVery hard
Lambda Layer with cryptographyModerate
Prebuilt deployment ZIPModerate
Container image LambdaLower✅ Best long-term
Custom resource calling external serviceDependsSometimes

Important technical detail

Python's standard library does not provide full X.509 certificate generation APIs.

So even though modules like:

import ssl
import hashlib

exist, they cannot generate ACM PCA-compatible certificates or CSRs.

You specifically need libraries like:

  • cryptography
  • pyOpenSSL

which require native binaries.


Best Practice (AWS perspective)

For certificate generation in Lambda, AWS best practice is usually:

Option 1 — Lambda Layer

Build the dependency on Amazon Linux compatible environment:

pip install cryptography -t python/
zip -r layer.zip python

Attach as a Lambda Layer.

Pros:

  • reusable
  • clean separation
  • smaller templates
  • production standard

Cons:

  • additional artifact

Option 2 — Container Image Lambda (Recommended for crypto-heavy workloads)

Example:

FROM public.ecr.aws/lambda/python:3.12

COPY requirements.txt .
RUN pip install -r requirements.txt

COPY app.py .

CMD ["app.handler"]

Pros:

  • deterministic builds
  • native libs work reliably
  • easiest long-term maintenance
  • ideal for PKI tooling

Cons:

  • no longer fully inline/self-contained

If portability is your real goal

A better architectural approach is usually:

CloudFormation
    ↓
Custom Resource
    ↓
Dedicated certificate Lambda/service

instead of embedding PKI logic directly into the template.

Reason:

X.509 generation is operationally complex:

  • entropy requirements
  • OpenSSL compatibility
  • wheel architecture issues
  • runtime ABI compatibility
  • future Python runtime changes

Trying to force this into ZipFile inline code becomes fragile very quickly.


One possible workaround (not ideal)

You could invoke AWS services directly instead of generating certificates locally.

For example:

  • ACM PCA IssueCertificate
  • AWS Private CA APIs
  • external CA endpoint

But if you truly need:

  • RSA keypair generation
  • CSR creation
  • self-signed certs
  • Client VPN cert chains

then you still need a crypto library somewhere.


Conclusion

cryptography cannot be used directly inside CloudFormation inline Lambda (ZipFile) because inline Lambdas cannot bundle compiled dependencies.

For X.509 operations on Lambda, the practical choices are:

  1. Lambda Layer
  2. Deployment package ZIP
  3. Container image Lambda (best overall)

There is no supported way to make cryptography magically available inside pure inline Python Lambda code.

answered 2 months ago

You are not logged in. Log in to post an answer.

A good answer clearly answers the question and provides constructive feedback and encourages professional growth in the question asker.