AWS Serverless Application Model (AWS SAM)

repository·develop·Indexed 27 days ago

https://github.com/aws/serverless-application-model

A CloudFormation macro and transform that allows developers to define serverless applications using simplified syntax, which is then expanded into standard CloudFormation resources. Includes the sam-translate CLI tool for converting, packaging, and deploying SAM templates, as well as documentation for local development, testing, and schema implementation.

Tokens
9.2K
Snippets
20
Records
43
Agent score
94%

What's inside AWS Serverless Application Model

  1. How the AWS SAM Transform works

    develop
    AWS SAM functions as a CloudFormation Transform. When you include the header Transform: AWS::Serverless-2016-10-31 in your template, the CloudFormation service invokes the SAM translator. The translator iterates through your template, identifies resources with the AWS::Serverless::* type, and expands them into a standard, full-fledged CloudFormation template. This expanded template is what CloudFormation uses to execute resource creation, updates, or deletions.
  2. Deploy a SAM template to AWS CloudFormation

    develop

    Deploy your packaged template to a CloudFormation stack. You can use aws cloudformation deploy or sam deploy. Both commands create and execute a ChangeSet. When deploying, you must specify --capabilities CAPABILITY_IAM if your template creates IAM resources.

    # Using AWS CLI
    $ aws cloudformation deploy \
        --template-file /path_to_template/packaged-template.yaml \
        --stack-name my-new-stack \
        --capabilities CAPABILITY_IAM
    
    # Using SAM CLI
    $ sam deploy \
        --template-file /path_to_template/packaged-template.yaml \
        --stack-name my-new-stack \
        --capabilities CAPABILITY_IAM
  3. Override Globals properties in resources

    develop

    Properties declared in Globals can be overridden by individual resources. The behavior depends on the data type:

    1. Primitive Values (String, Number, Boolean, etc.): The value in the resource replaces the global value.
    2. Maps (Dictionaries/Collections): Entries in the resource are merged with global entries. If a key exists in both, the resource entry overrides the global one.
    3. Lists (Arrays): Global entries are prepended to the list in the resource.
    # Primitive Override Example
    Globals:
      Function:
        Runtime: nodejs24.x
    Resources:
      MyFunction:
        Type: AWS::Serverless::Function
        Properties:
          Runtime: python3.14 # Replaces nodejs24.x
    
    # Map Merge Example
    Globals:
      Function:
        Environment:
          Variables:
            STAGE: Production
            TABLE_NAME: global-table
    Resources:
      MyFunction:
        Type: AWS::Serverless::Function
        Properties:
          Environment:
            Variables:
              TABLE_NAME: resource-table # Overrides global-table
              NEW_VAR: hello # Merged
    
    # List Prepend Example
    Globals:
      Function:
        VpcConfig:
          SecurityGroupIds:
            - sg-123
            - sg-456
    Resources:
      MyFunction:
        Type: AWS::Serverless::Function
        Properties:
          VpcConfig:
            SecurityGroupIds:
              - sg-first # Result: ["sg-123", "sg-456", "sg-first"]
  4. Configure code formatting with Black and pre-commit

    develop

    The project uses Black for code formatting.

    Automatic Installation

    Running make init installs Black automatically into your virtualenv.

    Using pre-commit

    To avoid manual formatting, you can use git hooks. After installing the pre-commit package, run: pre-commit install This will automatically run Black on every commit.

    IDE Integration Workaround

    If your IDE (like PyCharm) cannot find Black via the pyenv shim (/Users/<username>/.pyenv/shims/black), use the direct path to the binary in your virtualenv instead: /Users/<username>/.pyenv/versions/sam310/bin/black

  5. Use SAM Policy Templates for scoped permissions

    develop

    Instead of using broad AWS Managed Policies (like AmazonDynamoDBFullAccess), you can use SAM Policy Templates to grant your AWS::Serverless::Function specific, scoped permissions to resources within your stack.

    Policy Templates are more secure because they can be scoped to a specific resource (e.g., a single DynamoDB table) in the same region where your stack exists. SAM expands these templates into inline policy statements during deployment.

    To use a template, add it to the Policies property of your AWS::Serverless::Function resource. You can mix policy templates with AWS Managed Policies, custom managed policies, or inline policy statements.

    MyFunction:
      Type: AWS::Serverless::Function
      Properties:
        ...
        Policies:
          # Use a policy template scoped to a specific table
          - DynamoDBCrudPolicy:
              TableName: !Ref MyTable
        ...
    
    MyTable:
      Type: AWS::Serverless::SimpleTable
  6. Use the Globals section to share configuration

    develop

    Instead of duplicating shared configuration like Runtime, MemorySize, or Environment variables in every resource, you can define them once in the Globals section of your SAM template. All resources of the supported types will inherit these settings.

    Note that resources can override or extend global properties, but they cannot completely remove a property defined in the Globals section.

    Globals:
      Function:
        Runtime: nodejs24.x
        Timeout: 180
        Handler: index.handler
        Environment:
          Variables:
            TABLE_NAME: data-table
          
    Resources:
      HelloWorldFunction:
        Type: AWS::Serverless::Function
        Properties:
          Environment:
            Variables:
              MESSAGE: "Hello From SAM"
    
      ThumbnailFunction:
        Type: AWS::Serverless::Function
        Properties:
          Events:
            Thumbnail:
              Type: Api
              Properties:
                Path: /thumbnail
                Method: POST
  7. Implement new SAM resource properties

    develop

    When adding new properties to SAM resources, follow these rules to ensure stability and avoid transform failures:

    • For SAM resources: Use Property or PassThroughProperty instead of PropertyType. This prevents bugs and ensures valid templates do not cause transform failures.
    • For CloudFormation resources: Use GeneratedProperty. This performs no runtime validation, reducing the risk of valid values causing transform failures.
    • Location: Write all new code under samtranslator/internal whenever possible to avoid increasing the public library interface and causing unnecessary breakages for consumers.
  8. Implement PreTraffic and PostTraffic Hooks

    develop

    CodeDeploy allows you to run Lambda functions at specific lifecycle stages to validate deployments:

    • PreTraffic Hook: Runs before traffic shifting starts. The new Lambda version is created but not yet serving traffic. Use this to run integration tests against the new version.
    • PostTraffic Hook: Runs after traffic shifting completes. Use this for end-to-end validation or final integration checks.

    Critical Implementation Details:

    1. Asynchronous Execution: Hooks are invoked asynchronously. Your Lambda function must call the CodeDeploy API PutLifecycleEventHookExecutionStatus to report a Success or Failure. If the function fails or doesn't report, CodeDeploy will abort the deployment.
    2. SDK Requirement: Ensure your AWS SDK version supports PutLifecycleEventHookExecutionStatus (e.g., Python requires version 1.4.8 or newer).
    3. Permissions: Hook functions require specific permissions that are not included in standard Lambda execution roles:
      • codedeploy:PutLifecycleEventHookExecutionStatus on the deployment group resource.
      • lambda:InvokeFunction on the target Lambda function.
    4. Naming Convention: For the CodeDeploy service to invoke them, it is recommended to name your hook functions with the prefix CodeDeployHook_ (e.g., CodeDeployHook_preTrafficHook).
    5. Configuration: If the hook function is in the same SAM template, set DeploymentPreference: Enabled: False for the hook function itself to prevent recursive deployment loops.
    PreTrafficLambdaFunction:
      Type: AWS::Serverless::Function
      Properties:
        Handler: preTrafficHook.handler
        FunctionName: 'CodeDeployHook_preTrafficHook'
        DeploymentPreference:
          Enabled: False
        Policies:
          - Version: "2012-10-17"
            Statement:
            - Effect: "Allow"
              Action:
                - "codedeploy:PutLifecycleEventHookExecutionStatus"
              Resource:
                !Sub 'arn:${AWS::Partition}:codedeploy:${AWS::Region}:${AWS::AccountId}:deploymentgroup:${ServerlessDeploymentApplication}/*'
            - Version: "2012-10-17"
              Statement:
              - Effect: "Allow"
                Action:
                  - "lambda:InvokeFunction"
                Resource: !GetAtt MyLambdaFunction.Arn
        Runtime: nodejs24.x
        Environment:
          Variables:
            CurrentVersion: !Ref MyLambdaFunction.Version
  9. Manage multiple environments using CloudFormation Stacks

    develop

    To minimize the 'blast radius' and prevent accidental production changes, it is recommended to use a one-to-one mapping of environment to CloudFormation Stack.

    Use a single SAM template file and dynamically set the target stack for each environment using the --stack-name parameter during the aws cloudformation deploy command. For example, if you have dev, test, and prod environments, you would deploy to dev-stack, test-stack, and prod-stack respectively using the same template.

    If environments require different configurations, use a combination of Stack Parameters, Conditions, and Fn::If statements within your template.

  10. Set up a local development environment

    develop

    To develop on this project, you need to install multiple Python versions and set up a virtual environment.

    1. Install Python versions using pyenv

    Supported versions: 3.10, 3.11, 3.12, 3.13, 3.14.

    1. Install pyenv: curl -L https://github.com/pyenv/pyenv-installer/raw/master/bin/pyenv-installer | bash
    2. Restart your shell: exec $SHELL
    3. Install specific versions:
      • pyenv install 3.10.20
      • pyenv install 3.11.15
      • pyenv install 3.12.13
      • pyenv install 3.13.12
      • pyenv install 3.14.3
    4. Make them available locally: pyenv local 3.10.20 3.11.15 3.12.13 3.13.12 3.14.3

    Note for Windows users: pyenv is primarily for macOS/Linux. Consider using pipenv. If using pyenv on Windows via a compatibility layer, you may need to add the libexec path to your PATH: export PATH="/c/Users/<user>/.pyenv/libexec:$PATH".

    Ensure your .bashrc or .zshrc contains:

    export PATH="$HOME/.pyenv/bin:$PATH"
    eval "$(pyenv init -)"
    eval "$(pyenv virtualenv-init -)"

    2. Create and activate a virtualenv

    Create a dedicated environment for the project (e.g., sam310 for Python 3.10):

    1. pyenv virtualenv 3.10.16 sam310
    2. pyenv activate sam310

    3. Install the development version of SAM transform

    Once the virtualenv is active, install the project from source: make init