Header menu logo FsCDK

AWS Lambda Lambda quickstart

Spin up your first Lambda function with FsCDK using the same production-minded defaults promoted by AWS Heroes Yan Cui and Heitor Lessa. This quickstart walks through essential variations—memory, timeouts, environment variables, tracing—so you can go from “hello world” to secure, observable functions in minutes.

What you’ll practice

Prerequisites

Usage

1. Synthesize the CloudFormation template

cd examples/lambda-quickstart
dotnet build
cdk synth

This generates a CloudFormation template in cdk.out/ without requiring AWS credentials.

2. Deploy to AWS (sandbox account)

# Bootstrap CDK (first time only)
cdk bootstrap

# Deploy the stack
cdk deploy

3. Clean up

cdk destroy

What’s included

Default settings

The FsCDK Lambda builder mirrors the defaults promoted in Production-Ready Serverless:

Example 1: Basic function

#r "../src/bin/Release/net8.0/publish/Amazon.JSII.Runtime.dll"
#r "../src/bin/Release/net8.0/publish/Constructs.dll"
#r "../src/bin/Release/net8.0/publish/Amazon.CDK.Lib.dll"
#r "../src/bin/Release/net8.0/publish/System.Text.Json.dll"
#r "../src/bin/Release/net8.0/publish/FsCDK.dll"

open FsCDK
open Amazon.CDK
open Amazon.CDK.AWS.Lambda
open Amazon.CDK.AWS.Logs

lambda "my-function" {
    handler "index.handler"
    runtime Runtime.NODEJS_18_X
    code "./lambda-code"
}

Creates a function with all defaults.

Example 2: Custom memory and timeout

lambda "heavy-function" {
    handler "index.handler"
    runtime Runtime.PYTHON_3_11
    code "./lambda-code"
    memory 1024
    timeout 120.0
}

Adjusts memory and timeout for compute-intensive workloads.

Example 3: Environment variables

lambda "api-function" {
    handler "index.handler"
    runtime Runtime.DOTNET_8
    code "./publish"

    environment
        [ "DATABASE_URL", "postgres://localhost/mydb"
          "API_KEY", "secret-key"
          "LOG_LEVEL", "INFO" ]
}

Security Note: Environment variables are encrypted at rest using KMS by default.

Example 4: X-Ray tracing

lambda "traced-function" {
    handler "index.handler"
    runtime Runtime.NODEJS_20_X
    code "./lambda-code"
    xrayEnabled
    description "Function with X-Ray tracing for debugging"
}

Enables AWS X-Ray for distributed tracing.

Example 5: Cost optimization with custom ephemeral storage

let logGroupItm =
    logGroup "optimized-function-logs" { retention RetentionDays.THREE_DAYS }

lambda "optimized-function" {
    handler "index.handler"
    runtime Runtime.PYTHON_3_11
    code "./lambda-code"
    ephemeralStorageSize 1024 // Increase /tmp storage to 1 GB

    // For custom log retention, use logGroup builder:
    logGroup logGroupItm
}

Fine-tunes cost with custom ephemeral storage for workloads needing more than the default 512 MB /tmp space. Log retention is controlled via the logGroup builder.

Complete Example Stack

IAM Permissions

Default execution role

The builder automatically creates an IAM execution role with:

// Managed policy for CloudWatch Logs
"service-role/AWSLambdaBasicExecutionRole"

// Inline policy for KMS (when environment encryption enabled)
{
    "Effect": "Allow",
    "Action": "kms:Decrypt",
    "Resource": "arn:aws:kms:*:*:key/*"
}

Custom role

For advanced scenarios, bring your own execution role—handy when integrating with existing IAM governance models.

let customRole = IAM.createLambdaExecutionRole "my-function" true

lambda "my-function" {
    handler "index.handler"
    runtime Runtime.NODEJS_18_X
    code "./code"
    role customRole
}

Security Considerations

Environment variable encryption

All environment variables are encrypted at rest with KMS. This protects:

Best practice: Use AWS Secrets Manager or Parameter Store for highly sensitive secrets, matching the approach outlined in the AWS Security Blog.

Least-privilege IAM

The execution role includes only:

Add additional permissions explicitly:

open FsCDK.Security

let role = IAM.createLambdaExecutionRole "my-function" true
// Add S3 read access
IAM.allow ["s3:GetObject"] ["arn:aws:s3:::my-bucket/*"]
|> role.AddToPolicy

Log retention

Logs are retained for 1 week by default (via CloudWatch Log Groups) following Corey Quinn's cost optimization principle: "Never store logs forever." Balance retention with your needs:

To customize, use the logGroup builder: *

let logGrp = logGroup "MyFunction-logs" { retention RetentionDays.ONE_MONTH }

lambda "MyFunction" {
    handler "index.handler"
    runtime Runtime.NODEJS_18_X
    code "./code"

    logGroup logGrp
}

(*
## Performance Optimization

### Memory configuration

Lambda CPU scales with memory:

- **128-512 MB**: Low-power functions
- **512-1536 MB**: Standard workloads (default: 512 MB)
- **1536-10240 MB**: CPU-intensive tasks

### Ephemeral storage (/tmp)

Lambda provides 512 MB of /tmp storage for free. Increase when processing large files or caching data between invocations (cold starts reuse /tmp):

- **Default**: 512 MB (free)
- **Maximum**: 10,240 MB (charges apply above 512 MB)

Use `ephemeralStorageSize 1024` to customize.

### Timeout

Align timeouts with the latency guidance from **Yan Cui’s Production-Ready Serverless** series:

- **API handlers**: 5–30 seconds (FsCDK default is 30 s)
- **Batch processing**: 60–900 seconds
- **Upper bound**: 15 minutes (Lambda hard limit)

Always keep downstream service timeouts shorter, so the handler fails fast rather than waiting on hung dependencies.

### Cold-start optimisation

Adopt the techniques from **Alex Casalboni’s Lambda Power Tuning** workshop:
- Enable provisioned concurrency for latency-critical APIs.
- Keep deployment packages slim (leverage Lambda layers or bundlers like esbuild).
- Lazy-load heavy dependencies inside the handler instead of at module import time.

## Escape hatch

Need to drop down to raw CDK? `FunctionSpec` exposes the underlying props, so you can opt into niche configurations while still benefiting from FsCDK defaults.
*)

let funcSpec =
    lambda "my-function" {
        handler "index.handler"
        runtime Runtime.NODEJS_18_X
        code "./code"
    }
// Access props to see configuration
// The actual Function is created by the stack builder

Next steps

📚 Learning resources

All resources below are curated for quality (4.5★+ ratings or repeated recommendations by AWS Heroes).

Foundation (Week 0)

Hero insights & advanced reading

Performance & cost

Security & IAM

Observability

Suggested learning path

  1. Build this quickstart and review the generated CloudFormation.
  2. Enable Powertools and explore the tracing/logging features in Lambda Production Defaults.
  3. Model event-driven architectures with EventBridge and SNS SQS Messaging.
  4. Subscribe to Off-by-none (Jeremy Daly) and watch the latest re:Invent serverless sessions to stay current.

Community hubs

Continue practising by wiring these Lambdas into S3, DynamoDB, and EventBridge using the other FsCDK notebooks in this portal.

open Amazon.CDK
open Amazon.CDK.AWS.Lambda
open Amazon.CDK.AWS.Logs
open FsCDK

let app = App()

// Get environment configuration from environment variables
let accountId = System.Environment.GetEnvironmentVariable("CDK_DEFAULT_ACCOUNT")
let region = System.Environment.GetEnvironmentVariable("CDK_DEFAULT_REGION")

// Create stack props with environment
let envProps = StackProps()

if
    not (System.String.IsNullOrEmpty(accountId))
    && not (System.String.IsNullOrEmpty(region))
then
    envProps.Env <- Amazon.CDK.Environment(Account = accountId, Region = region)

envProps.Description <- "FsCDK Lambda Quickstart Example - demonstrates Lambda functions with security defaults"

// Create the stack
let stack = Stack(app, "LambdaQuickstartStack", envProps)

// Apply tags
Tags.Of(stack).Add("Project", "FsCDK-Examples")
Tags.Of(stack).Add("Example", "Lambda-Quickstart")
Tags.Of(stack).Add("ManagedBy", "FsCDK")

// Example 1: Basic function with all defaults
// Note: In a real scenario, provide actual code path
let basicFunc =
    lambda "basic-function" {
        handler "index.handler"
        runtime Runtime.NODEJS_18_X
        code "./dummy-code"
        description "Basic Lambda function with secure defaults"
    // Uses defaults:
    // - memorySize = 512 MB
    // - timeout = 30 seconds
    // - log retention = 1 week (via default logGroup)
    // - ephemeralStorageSize = 512 MB (free tier)
    // - environment encryption = KMS
    }

// Example 2: Function with custom memory and timeout
let computeFunc =
    lambda "compute-intensive-function" {
        handler "process.handler"
        runtime Runtime.PYTHON_3_11
        code "./dummy-code"
        memory 2048
        timeout 300.0
        description "Compute-intensive function with higher memory and timeout"
    }

// Example 3: Function with environment variables (encrypted by default)
let apiFunc =
    lambda "api-handler-function" {
        handler "api.handler"
        runtime Runtime.NODEJS_20_X
        code "./dummy-code"

        environment
            [ "LOG_LEVEL", "INFO"
              "API_VERSION", "v1"
              "REGION",
              (if System.String.IsNullOrEmpty(region) then
                   "us-east-1"
               else
                   region) ]

        description "API handler with encrypted environment variables"
    }

// Example 4: Function with X-Ray tracing enabled
let tracedFunc =
    lambda "traced-function" {
        handler "traced.handler"
        runtime Runtime.PYTHON_3_11
        code "./dummy-code"
        xrayEnabled
        description "Function with X-Ray tracing for debugging"
    }

// Example 5: Function with reserved concurrency
let rateLimitedFunc =
    lambda "rate-limited-function" {
        handler "ratelimited.handler"
        runtime Runtime.NODEJS_18_X
        code "./dummy-code"
        reservedConcurrentExecutions 10
        description "Function with reserved concurrent executions for rate limiting"
    }

app.Synth() |> ignore
namespace FsCDK
namespace Amazon
namespace Amazon.CDK
namespace Amazon.CDK.AWS
namespace Amazon.CDK.AWS.Lambda
namespace Amazon.CDK.AWS.Logs
val lambda: name: string -> FunctionBuilder
<summary>Creates a Lambda function configuration.</summary>
<param name="name">The function name.</param>
<code lang="fsharp"> lambda "MyFunction" { handler "index.handler" runtime Runtime.NODEJS_18_X code "./lambda" timeout 30.0 } </code>
custom operation: handler (string) Calls FunctionBuilder.Handler
<summary>Sets the handler for the Lambda function.</summary>
<param name="handler">The handler name (e.g., "index.handler").</param>
<code lang="fsharp"> lambda "MyFunction" { handler "index.handler" } </code>
custom operation: runtime (Runtime) Calls FunctionBuilder.Runtime
<summary>Sets the runtime for the Lambda function.</summary>
<param name="runtime">The Lambda runtime.</param>
<code lang="fsharp"> lambda "MyFunction" { runtime Runtime.NODEJS_18_X } </code>
Multiple items
type Runtime = inherit DeputyBase new: name: string * ?family: Nullable<RuntimeFamily> * ?props: ILambdaRuntimeProps -> unit member RuntimeEquals: other: Runtime -> bool member ToString: unit -> string member BundlingImage: DockerImage member Family: Nullable<RuntimeFamily> member IsVariable: bool member Name: string member SupportsCodeGuruProfiling: bool member SupportsInlineCode: bool ...

--------------------
Runtime(name: string, ?family: System.Nullable<RuntimeFamily>, ?props: ILambdaRuntimeProps) : Runtime
property Runtime.NODEJS_18_X: Runtime with get
custom operation: code (Code) Calls FunctionBuilder.Code
<summary>Sets the code source from a Code object.</summary>
<param name="path">The Code object.</param>
<code lang="fsharp"> lambda "MyFunction" { code (Code.FromBucket myBucket "lambda.zip") } </code>
property Runtime.PYTHON_3_11: Runtime with get
custom operation: memory (int) Calls FunctionBuilder.Memory
<summary>Sets the memory allocation for the Lambda function.</summary>
<param name="mb">The memory size in megabytes.</param>
<code lang="fsharp"> lambda "MyFunction" { memory 512 } </code>
custom operation: timeout (float) Calls FunctionBuilder.Timeout
<summary>Sets the timeout for the Lambda function.</summary>
<param name="seconds">The timeout in seconds.</param>
<code lang="fsharp"> lambda "MyFunction" { timeout 30.0 } </code>
property Runtime.DOTNET_8: Runtime with get
custom operation: environment ((string * string) list) Calls FunctionBuilder.Environment
<summary>Sets environment variables for the Lambda function.</summary>
<param name="env">List of key-value pairs for environment variables.</param>
<code lang="fsharp"> lambda "MyFunction" { environment [ "KEY1", "value1"; "KEY2", "value2" ] } </code>
property Runtime.NODEJS_20_X: Runtime with get
custom operation: xrayEnabled Calls FunctionBuilder.XRayEnabled
custom operation: description (string) Calls FunctionBuilder.Description
<summary>Sets the description for the Lambda function.</summary>
<param name="desc">The function description.</param>
<code lang="fsharp"> lambda "MyFunction" { description "Processes incoming orders" } </code>
val logGroupItm: CloudWatchLogGroupResource
val logGroup: name: string -> CloudWatchLogGroupBuilder
<summary> Creates a new CloudWatch Log Group builder with sensible defaults. Example: logGroup "/aws/ecs/my-service" { retention RetentionDays.ONE_MONTH } </summary>
custom operation: retention (RetentionDays) Calls CloudWatchLogGroupBuilder.Retention
[<Struct>] type RetentionDays = | ONE_DAY = 0 | THREE_DAYS = 1 | FIVE_DAYS = 2 | ONE_WEEK = 3 | TWO_WEEKS = 4 | ONE_MONTH = 5 | TWO_MONTHS = 6 | THREE_MONTHS = 7 | FOUR_MONTHS = 8 | FIVE_MONTHS = 9 ...
field RetentionDays.THREE_DAYS: RetentionDays = 1
custom operation: ephemeralStorageSize (int) Calls FunctionBuilder.EphemeralStorageSize
<summary> Sets the ephemeral storage size for the Lambda function in MB. Default: 512 MB (free tier). Valid range: 512-10240 MB. Cost optimization: Only increase if needed for /tmp storage. </summary>
<param name="sizeInMB">Storage size in megabytes (512-10240).</param>
<code lang="fsharp"> lambda "MyFunction" { ephemeralStorageSize 1024 } </code>
custom operation: logGroup (CloudWatchLogGroupResource) Calls FunctionBuilder.LogGroup
val get: unit -> {| Account: string; Region: string |}
namespace System
type Environment = static member Exit: exitCode: int -> unit static member ExpandEnvironmentVariables: name: string -> string static member FailFast: message: string -> unit + 1 overload static member GetCommandLineArgs: unit -> string array static member GetEnvironmentVariable: variable: string -> string + 1 overload static member GetEnvironmentVariables: unit -> IDictionary + 1 overload static member GetFolderPath: folder: SpecialFolder -> string + 1 overload static member GetLogicalDrives: unit -> string array static member SetEnvironmentVariable: variable: string * value: string -> unit + 1 overload static member CommandLine: string ...
<summary>Provides information about, and means to manipulate, the current environment and platform. This class cannot be inherited.</summary>
System.Environment.GetEnvironmentVariable(variable: string) : string
System.Environment.GetEnvironmentVariable(variable: string, target: System.EnvironmentVariableTarget) : string
val config: {| Account: string; Region: string |}
module Config from Lambda-quickstart
val stack: name: string -> StackBuilder
<summary>Creates an AWS CDK Stack construct.</summary>
<param name="name">The name of the stack.</param>
<code lang="fsharp"> stack "MyStack" { lambda myFunction bucket myBucket } </code>
val environment: EnvironmentBuilder
<summary>Creates an AWS CDK Environment configuration.</summary>
<code lang="fsharp"> environment { account "123456789012" region "us-west-2" } </code>
custom operation: account (string) Calls EnvironmentBuilder.Account
<summary>Sets the AWS account ID for the environment.</summary>
<param name="accountId">The AWS account ID.</param>
<code lang="fsharp"> environment { account "123456789012" } </code>
anonymous record field Account: string
custom operation: region (string) Calls EnvironmentBuilder.Region
<summary>Sets the AWS region for the environment.</summary>
<param name="regionName">The AWS region name.</param>
<code lang="fsharp"> environment { region "us-west-2" } </code>
anonymous record field Region: string
custom operation: description (string) Calls StackBuilder.Description
<summary>Sets the stack description.</summary>
<param name="config">The current stack configuration.</param>
<param name="desc">A description of the stack.</param>
<code lang="fsharp"> stack "MyStack" { description "My application stack" } </code>
custom operation: tags ((string * string) seq) Calls StackBuilder.Tags
<summary>Adds tags to the stack.</summary>
<param name="config">The current stack configuration.</param>
<param name="tags">A list of key-value pairs for tagging.</param>
<code lang="fsharp"> stack "MyStack" { tags [ "Environment", "Production"; "Team", "DevOps" ] } </code>
custom operation: reservedConcurrentExecutions (int) Calls FunctionBuilder.ReservedConcurrentExecutions
val customRole: AWS.IAM.Role
module IAM from FsCDK
<summary> IAM helpers for creating roles and policies following least-privilege principles. **Rationale:** - Least-privilege access reduces blast radius of compromised credentials - AWS managed policies provide maintained, secure defaults - Role-based access control simplifies permission management - Service-specific roles limit cross-service access **Best Practices:** - Always start with minimum required permissions - Use AWS managed policies when appropriate - Create custom policies for specific application needs - Avoid wildcards (*) in production - Review and audit IAM policies regularly </summary>
val createLambdaExecutionRole: functionName: string -> includeKmsDecrypt: bool -> AWS.IAM.Role
<summary> Creates a Lambda execution role with minimal permissions: - CloudWatch Logs write (basic execution) - Optional: KMS decrypt for encrypted environment variables </summary>
custom operation: role (AWS.IAM.IRole) Calls FunctionBuilder.Role
val logGrp: CloudWatchLogGroupResource
field RetentionDays.ONE_MONTH: RetentionDays = 5
val funcSpec: FunctionSpec
val app: App
Multiple items
type App = inherit Stage new: ?props: IAppProps -> unit static member IsApp: obj: obj -> bool

--------------------
App(?props: IAppProps) : App
val accountId: string
val region: string
val envProps: StackProps
Multiple items
type StackProps = interface IStackProps new: unit -> unit member AnalyticsReporting: Nullable<bool> member CrossRegionReferences: Nullable<bool> member Description: string member Env: IEnvironment member NotificationArns: string array member PermissionsBoundary: PermissionsBoundary member PropertyInjectors: IPropertyInjector array member StackName: string ...

--------------------
StackProps() : StackProps
Multiple items
type String = interface IEnumerable<char> interface IEnumerable interface ICloneable interface IComparable interface IComparable<string> interface IConvertible interface IEquatable<string> interface IParsable<string> interface ISpanParsable<string> new: value: nativeptr<char> -> unit + 8 overloads ...
<summary>Represents text as a sequence of UTF-16 code units.</summary>

--------------------
System.String(value: nativeptr<char>) : System.String
System.String(value: char array) : System.String
System.String(value: System.ReadOnlySpan<char>) : System.String
System.String(value: nativeptr<sbyte>) : System.String
System.String(c: char, count: int) : System.String
System.String(value: nativeptr<char>, startIndex: int, length: int) : System.String
System.String(value: char array, startIndex: int, length: int) : System.String
System.String(value: nativeptr<sbyte>, startIndex: int, length: int) : System.String
System.String(value: nativeptr<sbyte>, startIndex: int, length: int, enc: System.Text.Encoding) : System.String
System.String.IsNullOrEmpty(value: string) : bool
property StackProps.Env: IEnvironment with get, set
Multiple items
type Environment = interface IEnvironment new: unit -> unit member Account: string member Region: string

--------------------
Environment() : Environment
property StackProps.Description: string with get, set
val stack: Stack
Multiple items
type Stack = inherit Construct interface ITaggable new: ?scope: Construct * ?id: string * ?props: IStackProps -> unit member AddDependency: target: Stack * ?reason: string -> unit member AddMetadata: key: string * value: obj -> unit member AddStackTag: tagName: string * tagValue: string -> unit member AddTransform: transform: string -> unit member ExportStringListValue: exportedValue: obj * ?options: IExportValueOptions -> string array member ExportValue: exportedValue: obj * ?options: IExportValueOptions -> string member FormatArn: components: IArnComponents -> string ...

--------------------
Stack(?scope: Constructs.Construct, ?id: string, ?props: IStackProps) : Stack
type Tags = inherit DeputyBase member Add: key: string * value: string * ?props: ITagProps -> unit member Remove: key: string * ?props: ITagProps -> unit static member Of: scope: IConstruct -> Tags
Tags.Of(scope: Constructs.IConstruct) : Tags
val basicFunc: FunctionSpec
val computeFunc: FunctionSpec
val apiFunc: FunctionSpec
val tracedFunc: FunctionSpec
val rateLimitedFunc: FunctionSpec
Stage.Synth(?options: IStageSynthesisOptions) : CXAPI.CloudAssembly
val ignore: value: 'T -> unit

Type something to start searching.