Skip to content

Bug: Fn::Sub inside ZipFile inline Lambda code is evaluated at build time with wrong pseudo-parameter values in 1.160.x #9029

@johan-circumvent

Description

@johan-circumvent

Description:

When a template uses AWS::LanguageExtensions as a transform and has an AWS::Lambda::Function resource with inline ZipFile code containing !Sub (to interpolate ${AWS::Region} or ${AWS::AccountId}), sam build evaluates the Fn::Sub at build time rather than leaving it as a CloudFormation intrinsic for deployment-time resolution.

The result is that the built template has the substitutions baked in using whatever defaults the build environment has — typically us-east-1 for region and 123456789012 for account ID — instead of the actual deployment target values. The Fn::Sub wrapper is stripped entirely from the built template.

This is a regression introduced in 1.160.0 alongside the in-tree CloudFormation Language Extensions resolver.

Steps to reproduce:

  1. Create template.yaml:
Transform:
  - AWS::LanguageExtensions
  - AWS::Serverless-2016-10-31

Resources:
  MyTriggerFunction:
    Type: AWS::Lambda::Function
    Properties:
      FunctionName: my-trigger
      Runtime: python3.11
      Handler: index.handler
      Role: !Sub 'arn:aws:iam::${AWS::AccountId}:role/my-role'
      Code:
        ZipFile: !Sub |
          import boto3
          def handler(event, context):
              client = boto3.client('stepfunctions')
              client.start_execution(
                  stateMachineArn='arn:aws:states:${AWS::Region}:${AWS::AccountId}:stateMachine:my-state-machine',
              )
  1. Run sam build
  2. Inspect .aws-sam/build/template.yaml

Observed result:

The built template has Fn::Sub stripped and ${AWS::Region} / ${AWS::AccountId} resolved to build-time defaults:

Code:
  ZipFile: "import boto3\ndef handler(event, context):\n    client = boto3.client('stepfunctions')\n\
    \    client.start_execution(\n        stateMachineArn='arn:aws:states:us-east-1:123456789012:stateMachine:my-state-machine',\n\
    \    )\n"

Note: Fn::Sub in other parts of the template (e.g. Role: !Sub '...') is correctly preserved as a CloudFormation intrinsic and not evaluated at build time.

Expected result:

The built template should preserve Fn::Sub as a CloudFormation intrinsic, identical to the behaviour in 1.148.0:

Code:
  ZipFile:
    Fn::Sub: "import boto3\ndef handler(event, context):\n    client = boto3.client('stepfunctions')\n\
      \    client.start_execution(\n        stateMachineArn='arn:aws:states:${AWS::Region}:${AWS::AccountId}:stateMachine:my-state-machine',\n\
      \    )\n"

CloudFormation should then resolve ${AWS::Region} and ${AWS::AccountId} to the actual deployment region and account at stack create/update time.

Additional environment details

Working: SAM CLI 1.148.0
Broken: SAM CLI 1.160.0, 1.160.1

The regression appears to be tied to the introduction of the in-tree cfn_language_extensions resolver in 1.160.0 (see #9010 for a related fix in the same area). The template must have AWS::LanguageExtensions in its Transform list to trigger the issue — templates without it are unaffected.

  1. OS: macOS (arm64)
  2. sam --version: 1.160.1 (broken), 1.148.0 (working)
  3. AWS region: ap-southeast-2

SAM 1.160.1:

{
  "version": "1.160.1",
  "system": {
    "python": "3.13.9",
    "os": "macOS-26.5-arm64-arm-64bit-Mach-O"
  }
}

SAM 1.148.0:

{
  "version": "1.148.0",
  "system": {
    "python": "3.11.10",
    "os": "macOS-26.5-arm64-arm-64bit"
  }
}

Metadata

Metadata

Assignees

No one assigned

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions